X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?p=userv.git;a=blobdiff_plain;f=spec.sgml.in;fp=spec.sgml.in;h=5fc9ce6348edbe83bd2b17be75d903d76c16d081;hp=0000000000000000000000000000000000000000;hb=9f9813ee612b45b0bb64345b482899fa3a39b5a5;hpb=26444956110f28281bef77dfc28be75e25ae85a2 diff --git a/spec.sgml.in b/spec.sgml.in new file mode 100644 index 0000000..5fc9ce6 --- /dev/null +++ b/spec.sgml.in @@ -0,0 +1,1355 @@ + + + +User service daemon and client specification +<author>Ian Jackson <email>ian@chiark.greenend.org.uk +<version></version> + +<abstract> +This is a specification for a Unix system facility to allow one +program to invoke another when only limited trust exists +between them. + +<copyright> +Copyright 1996-1997 Ian Jackson. +<p> + +<prgn/userv/ is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. +<p> + +This program is distributed in the hope that it will be useful, but +<em/without any warranty/; without even the implied warranty of +<em/merchantability/ or <em/fitness for a particular purpose/. See +the GNU General Public License for more details. +<p> + +You should have received a copy of the GNU General Public License +along with <prgn/userv/; if not, write to the Free Software +Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +<toc sect> + +<chapt id="intro">Introduction +<p> +There is a daemon which invokes user service programs (henceforth +`services') in response to requests by callers of a companion client +program (henceforth the `client') and according to rules set forth in +system-wide and user-specific configuration files. The companion +client program is setuid root, and negotiates with the daemon through +an <prgn/AF_UNIX/ socket and associated objects in a system-wide +private directory set aside for the purpose. The user who wishes the +service to be performed and calls the client is called the `calling +user'; the process which calls the client is called the `calling +process'. + +<p> +The daemon and the client are responsible for ensuring that +information is safely carried across the security boundary between the +two users, and that the processes on either side cannot interact with +each other in any unexpected ways. + +<chapt id="client">Client program usage + +<p> +<example> +userv <var/options/ [--] <var/service-user/ <var/service-name/ [<var/argument/ ...] +userv <var/options/ -B|--builtin [--] <var/builtin-service/ [<var/info-argument/ ...] +</example> +<p> + +<var/service-user/ specifies which user is to provide the service. +The user may be a login name or a numeric uid, or <tt/-/ to indicate +that the service user is to be the same as the calling user. +<p> + +The service name is interpreted by the userv<footnote><prgn/userv/ is +short for `user services', and is pronounced `you-serve'.</footnote> +daemon on behalf of the service user. It will often be the name of a +program. + +<sect>Options +<p> + +Single-letter options may be combined as is usual with Unix programs, +and the value for such an option may appear in the same argument or in +the next. + +<taglist> +<tag/<tt/-B// +<tag/<tt/--builtin// +<item> +Requests that a builtin service be provided. This is equivalent to +using the <prgn/--override/ option to specify a string consisting of +<prgn/execute-builtin/ followed by the <var/builtin-service/ +requested, and requesting a service user of <tt/-/ (indicating the +calling user). +<p> + +If the builtin service being requested requires a +<var/service-argument/ then this must be supplied to the client in the +same argument as the <var/builtin-service/. See <ref +id="dirs-execution"> for details of the builtin services available, +and <ref id="optoverride"> for details of the <prgn/--override/ +options. +<p> + +The actual service name passed will be the <var/builtin-service/; note +that this actual service name (as opposed to the override data) and +the <var/info-argument/s supplied will be ignored by most builtin +services; the override mechanism and <prgn/execute-builtin/ will be +used to ensure that the right builtin service is called with the right +<var/service-argument/s. + +<tag/<tt/-f<var/fd/[<var/modifiers/]=<var/filename/// +<tag/<tt/--file <var/fd/[<var/modifiers/]=<var/filename/// +<item> +Requests that data be copied in and out of the service using pipes. +For each file or descriptor this will be done by creating a pipe, one +end of which is passed to the service program and the other end of +which is passed to a copy of <prgn/cat/ invoked by the client; the +other file descriptor passed to <prgn/cat/ will be one inherited by +the client program from the caller or one opened by the client program +on behalf of the caller. +<p> + +The descriptor in the service program that should be connected must be +specified as <var/fd/, either as a decimal number or as one of the +strings <tt/stdin/, <tt/stdout/ or <tt/stderr/. The next argument is +a filename which will be opened by the client with the privileges of +the calling user. + +<p> +<var/modifiers/ is used to specify whether the file or descriptor is +to be read from or written to. It consists of a series of words +separated by commas. A comma may separate the <var/modifiers/ from +the <var/fd/ and is required if <var/fd/ is not numeric. + +<p> +The modifier words are: +<taglist compact> +<tag/<tt/read// +<item> +<tt/O_RDONLY/: Allow reading and not writing. May not be used with +<tt/write/ or things that imply it. + +<tag/<tt/write// +<item> +<tt/O_WRONLY/: Allow writing and not reading. <em/Doesn't truncate or +create/ without <tt/truncate/ or <tt/create/. <tt/write/ or things +that imply it may not be used with <tt/read/. + +<tag/<tt/overwrite// +<item> +Equivalent to <tt/write,create,truncate/. + +<tag/<tt/create// +<tag/<tt/creat// +<item> +<tt/O_CREAT/: Creates the file if necessary. Implies <tt/write/. + +<tag/<tt/exclusive// +<tag/<tt/excl// +<item> +<tt/O_EXCL/: Fails if the file already exists. Implies <tt/write/ and +<tt/create/. May not be used with <tt/truncate/. + +<tag/<tt/truncate// +<tag/<tt/trunc// +<item> +<tt/O_TRUNC/: Truncate any existing file. Implies <tt/write/. +May not be used with <tt/exclusive/. + +<tag/<tt/append// +<item> +<tt/O_APPEND/: All writes will append to the file. Implies <tt/write/ +(but not <tt/create/). + +<tag/<tt/sync// +<item> +<tt/O_SYNC/: Do writes synchronously. Implies <tt/write/. + +<tag/<tt/wait// +<tag/<tt/nowait// +<tag/<tt/close// +<item> + +These modifiers control the behaviour of the client, with respect to +the pipes carrying data to and from the service, when the service +terminates. See below. + +<tag/<tt/fd// +<item> +The <var/filename/ is not a filename but a numeric file descriptor. +One or both of <tt/read/ and <tt/write/ must be specified, and no +other words are allowed. The <var/filename/ may also be <tt/stdin/, +<tt/stdout/ or <tt/stderr/ for file descriptor 0, 1 or 2 respectively. + +</taglist> +<p> + +If no <var/modifiers/ which imply <tt/read/ or <tt/write/ are used it +is as if <tt/read/ had been specified, except that if the +filedescriptor 1 or 2 of the service is being opened (either specified +numerically or with <tt/stdout/ or <tt/stderr/) it is as if +<tt/overwrite/ had been specified (or <tt/write/ if only <tt/fd/ was +specified). +<p> + +The client will also use <tt/O_NOCTTY/ when opening files specified by +the caller, to avoid changing its controlling terminal. +<p> + +By default stdin, stdout and stderr of the service will be connected +to the corresponding descriptors on the client. Diagnostics from +the client and daemon will also appear on stderr. +<p> + +If <tt/wait/ is specified, the client will wait for the pipe to be +closed, and only exit after this has happened. This means that either +the receiving end of the pipe connection was closed while data was +still available at the sending end, or that the end of file was +reached on the reading file descriptor. Errors encountered reading or +writing in the client at this stage will be considered a system error +and cause the client to exit with status 255, but will not cause +disconnection at the service side since the service has already +exited. +<p> + +If <tt/close/ is specified the client will immediately close the pipe +connection by killing the relevant copy of <prgn/cat/. If the service +uses the descriptor it will get <prgn/SIGPIPE/ (or <prgn/EPIPE/) for a +writing descriptor or end of file for a reading one; the descriptor +opened by or passed to the client will also be closed. +<p> + +If <tt/nowait/ is specified then the client will not wait and the +connection will remain open after the client terminates. Data may +continue to be passed between the inheritors of the relevant +descriptor on the service side and the corresponding file or +descriptor on the client side until either side closes their +descriptor. This should not usually be specified for stderr (or +stdout if <tt/--signals stdout/ is used) since diagnostics from +the service side may arrive after the client has exited and be +confused with expected output. +<p> + +The default is <tt/wait/ for writing file descriptors and <tt/close/ +for reading ones. + +<tag/<tt/-w<var/fd/=<var/action/// +<tag/<tt/--fdwait<var/fd/=<var/action/// +<item> +Sets the action on termination of the service for the specified file +descriptor; <var/action/ must be <tt/wait/, <tt/nowait/ or <tt/close/ +as described above. The file descriptor must be specified as open +when this option is encountered; this option is overridden by any +later <prgn/--file/ or <prgn/--fdwait/ option - even by a +<prgn/--file/ which does not specify an action on termination (in this +case the default will be used, as described above). + +<tag/<tt/-D<var/name/=<var/value/// +<tag/<tt/--defvar <var/name/=<var/value/// +<item> +Set a user-defined variable <var/name/ to <var/value/. These +user-defined variables are made available in the configuration +language as the parameters <tt/u-<var/name// and are passed to the +service in environment variables <tt/USERV_U_<var/name//. <var/name/ +may contain only alphanumerics and underscores, and must start with a +letter. If several definitions are given for the same <var/name/ then +only the last is effective. + +<tag/<tt/-t <var/seconds/// +<tag/<tt/--timeout <var/seconds/// +<item> +Time out the service if it takes longer than <var/seconds/ seconds (a +positive integer, in decimal). Timeout will produce a diagnostic on +stderr and an exit status of 255. If <var/seconds/ is zero then no +timeout will be implemented (this is the default). + +<tag/<tt/-S/ <var/method// +<tag/<tt/--signals/ <var/method// +<item> +Affects the handling of the exit status when the service terminates +due to a signal. (The client will always finish by calling +<prgn/_exit/, so that only numbers from 0 to 255 can be returned and +not the full range of numbers and signal indications which can be +returned by the <prgn/wait/ family of system calls.) +<p> + +The <var/method/ may be one of the following: +<taglist compact> +<tag/<var/status/ +<item> +The client's exit status will be <var/status/. This will not be +distinguishable from the service really having exited with code +<var/status/. This method is the default, with a <var/status/ of 254. + +<tag/<tt/number// +<tag/<tt/number-nocore// +<item> +The client's exit status will be the number of the signal which caused +the termination of the service. If <tt/number/ is used rather than +<tt/number-nocore/ then 128 will be added if the service dumped core. +<tt/number/ is very like the exit code mangling done by the Bourne +shell. + +<tag/<tt/highbit// +<item>The client's exit status will be the number of the signal with +128 added. If the service exits normally with an exit code of greater +than 127 then 127 will be returned. + +<tag/<tt/stdout// +<item> +The service's numeric wait status as two decimal numbers (high byte +first) and a textual description of its meaning will be printed to the +client's standard output. It will be preceded by a newline and +followed by an extra newline, and the numbers are separated from each +other and from the textual description by single spaces. The exit +status of the client will be zero, unless a system error occurs in +which case no exit status and description will be printed to stdout, +and an error message will be printed to stderr as usual. +</taglist> + +<p> +Problems such as client usage errors, the service not being found or +permission being denied or failure of a system call are system errors. +An error message describing the problem will be printed on the +client's stderr, and the client's exit status will be 255. If the +client dies due to a signal this should be treated as a serious system +error. + +<tag/<tt/-H// +<tag/<tt/--hidecwd// +<item> +Prevents the calling process's current directory name from being +passed to the service; the null string will be passed instead. + +<tag/<tt/-P// +<tag/<tt/--sigpipe// +<item> +If the service program is terminated due to a <prgn/SIGPIPE/ the exit +status of the client will be zero, even if it would have been +something else according to the exit status method specified. This +option has no effect on the code and description printed if the exit +status method <tt/stdout/ is in use. + +<tag/<tt/-h// +<tag/<tt/--help// +<tag/<tt/--copyright// +<item> +<tt/-h/ or <tt/--help/ prints the client's usage message; +<tt/--copyright/ prints the copyright and lack of warranty notice. + +</taglist> + +<sect id="optoverride">Security-overriding options +<p> + +There are also some options which are available for debugging and to +allow the system administrator to override a user's policy. These +options are available only if the client is called by root or if the +calling user is the same as the service user. + +<taglist> + +<tag/<tt/--override <var/configuration-data/// +<tag/<tt/--override-file <var/filename/// +<item> +Do not read the usual configuration files. Instead, the client sends +<var/configuration-data/ (followed by a newline) or the contents of +<var/filename/ (which is opened in the context of the client) to the +daemon and the daemon uses that data instead. The +<var/configuration-data/ must all be in one argument. It will have a +single newline appended so that a single directive can easily be +given, but if more than one directive is required it will have to +contain one or more real newlines. + +<tag/<tt/--spoof-user <var/user/// +<item> +Pretend to the service that it is being called by <var/user/ (which +may be a username or a uid). This will also affect the group and +supplementary groups supplied to the service; they will be the +standard group and supplementary groups for <var/user/. + +</taglist> + + +<chapt id="envir">Execution environment of the service program +<p> + +The daemon which is handling the service user side of things will read +configuration files to decide what to do. If it decides to allow the +service to be provided it will fork a subprocess to execute the +service. +<p> + +The service will have no controlling terminal, but it will be a +process group leader. +<p> + +If the client is killed or times out or a file or descriptor being +read or written by the client process gets an error then the service +will be disconnected from the client. The client will return an exit +status of 255 and some the service's pipes may be closed at the other +end. The service will become a child of <prgn/init/. The service may +well not notice the disconnection, though writing to a pipe after this +may produce a <prgn/SIGPIPE/ and the facility exists to have a +<prgn/SIGHUP/ sent to the service on disconnection. + +<sect>File descriptors +<p> + +The service program's standard filedescriptors, and possibly other +file descriptors, will be connected to pipes or to +<prgn>/dev/null</>. The <prgn/userv/ client/daemon pair will arrange +that data is copied between the files or file descriptors specified to +to the client by the caller and these these pipes. +<p> + +Pipes which may be written to will be closed if a write error occurs +on the corresponding client-side file or descriptor, which may result +in a <prgn/SIGPIPE/ in the service program; pipes open for reading +will get <prgn/EOF/ if the client-side file descriptor gets <prgn/EOF/ +or an error. +<p> + +If the service closes one of its reading file descriptors the writing +end of the corresponding pipe will generate a <prgn/SIGPIPE/ when +attempts are made by the client/daemon pair to write to it. This will +not be considered an error; rather, the relevant pipe will be +discarded and the corresponding file or file descriptor held by the +client will be closed. +<p> + +Likewise, if one of the file descriptors held by the client for +writing by the service is a pipe whose other end is closed by the +caller then the client/daemon pair will see an error when trying to +copy data provided by the service. This too will not be considered an +error; rather, the pipe correspondong to that descriptor will be +closed and any further writes will cause the service to get a +<prgn/SIGPIPE/. +<p> + +Note that not all write errors or broken pipes on file descriptors may +be visible to the service, since buffered data may be discarded by the +operating system and there will be a finite interval between the error +happening and the service being disconnected from the client or the +next write causing a <prgn/SIGPIPE/. +<p> + +Read errors on file descriptors (and disconnection) will only be +visible to the service and distinguishable from normal end of file if +<prgn/disconnect-hup/ is in effect. +<p> + +Read and write errors (other than broken pipes, as described above) +will always be visible to the caller; they are system errors, and will +therefore cause the client to print an error message to stderr and +return with an exit status of 255. +<p> + +If the main service program process exits while it still has running +children any file descriptors held by those children can remain open, +depending on the use of <tt/wait/, <tt/nowait/ or <tt/close/ for the +relevant file descriptor in the client's arguments. By default +writing filedescriptors remain open and the client will wait for them +to be closed at the service end, and reading file descriptors are +closed immediately. These leftover child processes will not get a any +<prgn/SIGHUP/ even if a read or write error occurs or the client +disconnects before then. + +<sect>Environment +<p> + +The service will have some information in environment variables: +<taglist compact> +<tag/<tt/USERV_USER// +<item> +The login name of the calling user. If the <prgn/LOGNAME/ variable is +set (or, if that is unset, if the <prgn/USER/ variable is set) in the +environment passed to the client by the caller then the password entry +for that login name will be looked up; if that password entry's uid is +the same as that of the calling process then that login name will be +used, otherwise (or if neither <prgn/LOGNAME/ nor <prgn/USER/ is set) +the calling process's uid will be looked up to determine their login +name (and if this lookup fails then the service will not be invoked). + +<tag/<tt/USERV_UID// +<item> +The uid of the calling process. + +<tag/<tt/USERV_GID// +<item> +The gid and supplementary group list of the calling process: first the +group in gid and then those in the supplementary group list, in +decimal, separated by spaces. + +<tag/<tt/USERV_GROUP// +<item> +The group names of the calling process, listed in the same way as the +ids are in <prgn/USERV_GID/. If no name can be found for any of the +calling process's group(s) then the service will not be invoked. + +<tag/<tt/USERV_CWD// +<item> +The client's current working directory name (this directory may not be +accessible to the service). If it could not be determined or the +<prgn/--hidecwd/ flag was used then this variable will be set to an +empty string (this is not considered an error). + +<tag/<tt/USERV_SERVICE// +<item> +The service name requested by the caller. + +<tag/<tt/USERV_U_<var/name/// +<item> +The value supplied to the client by the caller using -D<var/name/. + +</taglist> + +<prgn/HOME/, <prgn/PATH/, <prgn/SHELL/, <prgn/LOGNAME/ and <prgn/USER/ +will be set appropriately (according to the details of the service +user). + + +<chapt id="config">Service-side configuration +<p> + +Which services may be run by whom and under what conditions is +controlled by configuration files. +<p> + +The daemon will read these files in order. Certain directives in the +files modify the daemon's execution settings for invoking the service, +for example allowing certain file descriptors to be specified by the +client or specifying which program to execute to provide the service. +<p> + +The <em/last/ instance of each such setting will take effect. The +directives which specify which program to execute will not stop the +configuration file from being read; they will be remembered and will +only take effect if they are not overridden by a later directive. +<p> + +The daemon will first read <tt>/etc/userv/system.default</>. Then, by +default (this behaviour may be modified), it will read a per-user file +<tt>~/.userv/rc</>, if it exists and the service user's shell is in +<tt>/etc/shells</>. Finally it will read +<tt>/etc/userv/system.override</>. +<p> + +When it has read all of these files it will act according to the +currently values of of the execution settings. + +<sect>Configuration file syntax +<p> + +The configuration file is a series of directives, usually one per +line. The portion of a line following a hash character <tt/#/ is +taken as a comment and ignored. Each directive consists of a series +of tokens separated by linear whitespace (spaces and tabs); tokens may +be words consisting of non-space characters, or, where a string is +required, a string in double quotes. Double-quoted strings may +contain the following backslash escapes: + +<taglist compact> +<tag/<tt/\n//<item>newline +<tag/<tt/\t//<item>tab +<tag/<tt/\r//<item>carriage return +<tag/<tt/\<var/OOO///<item>character whose octal code is <var/OOO/ +<tag/<tt/\x<var/XX///<item>character whose hex code is <var/XX/ +<tag/<tt/\<var/punctuation///<item>literal punctuation character (eg <tt/\\/, <tt/\"/) +<tag/<tt/\<var/newline// (ie, backslash at end of line)/ +<item>string continues on next line +</taglist> +<p> + +Relative pathnames in directives are relative to the service program's +current directory (usually the service user's home directory). +Pathnames starting with the two characters <tt>~/</> are taken to be +relative to the service user's home directory. + +<sect id="directives">Configuration file directives +<p> + +<sect1 id="dirs-immediate">Immediate directives +<p> + +The following directives take effect immediately: + +<taglist> +<tag/<tt/cd <var/pathname/// +<item> +Change directory in the service program. <prgn/cd/ is cumulative. It +is an error if the directory cannot be changed to. +<p> + +<prgn/cd/ should not be used between <prgn/execute-from-directory/ and +the invocation of the service program, as the test for the +availability of the service program would be done with the old current +directory and the actual execution with the new (probably causing an +error). + +<tag/<tt/eof// +<item> +Stop reading the configuration file in question, as if end of file had +been reached. Any control constructs (<prgn/if/, <prgn/catch-quit/ or +<prgn/errors-push/) which were started in that file will be considered +finished. Parsing will continue in the file which caused the file +containing the <prgn/eof/ to be read. + +<tag/<tt/quit// +<item> +Stop reading configuration files and act immediately on the current +settings. The behaviour of <prgn/quit/ is subject to the +<prgn/catch-quit/ control construct. + +<tag/<tt/include <var/filename/// +<tag/<tt/include-ifexist <var/filename/// +<item> +Read the configuration file <var/filename/, and then return to this +file and continue parsing it with the next directive. It is an error +if the file cannot be opened and read, unless <prgn/include-ifexist/ +is used and the file does not exist, in which case the directive is +silently ignored. + +<tag/<tt/include-lookup <var/parameter/ <var/directory/// +<tag/<tt/include-lookup-all <var/parameter/ <var/directory/// +<item> +Read the configuration file in <var/directory/ whose name is the value +of <var/parameter/ (see the description of <prgn/if/, <ref +id="dirs-control">). If <var/parameter/ has several values they will +be tried in order; with <prgn/include-lookup/ this search will stop +when one is found, but with <prgn/include-lookup-all/ the search will +continue and any files appropriate to other values will be read too. +<p> + +If none of the parameter's values had a corresponding file then the +file <tt/:default/ will be read, if it exists. If <var/parameter/'s +list of values was empty then the file <tt/:none/ will be tried first +and read if it exists, otherwise <tt/:default/ will be tried. +<p> + +It is not an error for any of the files (including <tt/:default/) not +to exist, but it is an error if a file exists and cannot be read or if +the directory cannot be accessed. + +<p> +A translation will be applied to values before they are used to +construct a filename, so that the lookup cannot access dotfiles or +files in other directories: values starting with full stops will have +a colon prepended (making <tt/:./), colons will be doubled, and each +slash will be replaced with a colon followed by a hyphen <tt>:-</>. A +parameter value which is the empty string will be replaced with +<tt/:empty/ (note that this is different from a parameter not having +any values). + +<tag/<tt/include-directory <var/directory/// +<item> +Read configuration from all files in directory <var/directory/ which +are plain files whose names consist only of alphanumerics and hyphens +and start with an alphanumeric. They will be read in lexical order. +It is an error for the directory not to exist or for it or any of the +files found not to be read successfully, or for anything with an +appropriate name not to be a plain file or a symbolic link to a plain +file. + +<tag/<tt/error <var/text .../// +<item> +Causes an error whose message includes the descriptive string +<var/text/. <var/text/ may consist of several tokens with intervening +whitespace. The whitespace will be included in the message as found +in the configuration file: all the characters until the end of the +line will be included verbatim, unless they are part of a +double-quoted string, in which case the usual meaning of the string +(i.e., after backslash escape processing) will be used. Comments and +linear whitespace at the end of the line (or just before the comment) +will still be ignored. + +<tag/<tt/message <var/text .../// +<item> +Causes a message including the descriptive string <var/text/ to be +delivered as if it were an error message, but does not actually cause +an error. +</taglist> + +<sect1 id="dirs-delayed">Directives with delayed effect +<p> + +The following directives have no immediate effect, but are remembered +and have an effect on later processing of the configuration files. + +<taglist> +<tag/<tt/user-rcfile <var/filename/// +<item> +Specifies that the file <var/filename/ should be read instead of the +user's <tt>~/.userv/rc</>. This does <em/not/ happen immediately; +instead, the setting is remembered and used after the +<prgn/system.default/ configuration file has been read. This +directive has no effect in a user's configuration file or in the +<prgn/system.override/ file, as the user's configuration file has +already been found and read by then and will not be re-read. + +<tag/<tt/errors-to-stderr// +<item> +Causes error messages to be delivered to the client's stderr. + +<tag/<tt/errors-to-file/ <var/filename// +<item> +Error messages will be written to <var/filename/, which will be opened +in the context of and with the privileges of the service user. + +<tag/<tt/errors-to-syslog/ [<var/facility/ [<var/level/]]/ +<item> +Error messages will be delivered using <prgn/syslog/. The default +<var/facility/ is <tt/daemon/; the default <var/level/ is <tt/error/. +</taglist> + +<sect1 id="dirs-control">Control structure directives +<p> + +The following directives are used to create control structures. If +the end of the file is encountered before the end of any control +structure which was started inside it then that control structure is +considered finished. This is not an error. + +<taglist> +<tag/<tt/if <var/condition/// +<tag/<tt/elif <var/condition/// +<tag/<tt/else// +<tag/<tt/fi// +<item> +Lines following <prgn/if/ are interpreted only if the condition is +true. Many conditions are properties of parameter values. Most +parameters have a single string as a value; however, some may yield +zero or several strings, in which case the condition is true if it is +true of any of the strings individually. Parameters are described +below. +<p> + +The conditions are: + +<taglist compact> +<tag/<tt/glob <var/parameter/ <var/glob-pattern/ ...// +<item> +The value of the parameter whose name is given matches one of the glob +patterns (anchored at both ends; backslashes can be used to escape +metacharacters). + +<tag/<tt/range <var/parameter/ <var/min/ <var/max/// +<item> +The value of the parameter is a nonnegative integer and lies within +the range specified. <var/min/ or <var/max/ may be <tt/$/ to indicate +no lower or upper limit, respectively. + +<tag/<tt/grep <var/parameter/ <var/filename/// +<item> +The <var/filename/ refers to a file one of whose lines is the value of +the parameter (leading or trailing whitespace on each line and empty +lines in the file are ignored). It is an error for the file not to be +opened and read. + +<tag/<tt/! <var/condition/// +<item> +The <var/condition/ is <em/not/ true. + +<tag/Conjunctions: <tt/&/ and <tt/|// +<item> +<example> +( <var/condition/ +& <var/condition/ +& <var/condition/ +... +) +</example> +is true if all the listed conditions are true; where <tt/|/ is used it +is true if any of them is true. Newlines must be used to separate one +condition from the next, as shown, and the parentheses are mandatory. +These conjunctions do not do lazy evaluation. +</taglist> +<p> + +The parameters are: + +<taglist compact> +<tag/<tt/service// +<item> +The service name specified when the client was called. + +<tag/<tt/calling-user// +<item> +Two strings: the login name of the calling user (determined as for +<prgn/USERV_USER/, above) and the calling uid (represented in +decimal). + +<tag/<tt/calling-group// +<item> +Several strings: the primary and supplementary group names and gids +(in decimal) of the calling process. All the group names come first, +and then the gids. If the first supplementary group is the same as +the primary group then it is elided. + +<tag/<tt/calling-user-shell// +<item> +The calling user's shell, as listed in the password entry for the +calling login name (as determined for <prgn/USERV_USER/, above). + +<tag/<tt/service-user// +<item> +Two strings: the name of the service user (as specified to the client) +and their uid (represented in decimal). + +<tag/<tt/service-group// +<item> +Several strings: the primary and supplementary group names and gids +(in decimal) of the service user. + +<tag/<tt/service-user-shell// +<item> +The service user's shell, as listed in their password entry. + +<tag/<tt/u-<var/name/// +<item> +The value of the user-defined variable <var/name/ passed by the caller +using the <prgn/--defvar/ command-line option to the client. If the +variable was not defined then this parameter is an empty list of +strings; in this case any condition which tests it will be false, and +<tt/include-lookup/ on it will read the <tt/:none/ file, or +<tt/:default/ if <tt/:none/ is not found. + +</taglist> + +<tag/<tt/errors-push/ <var/filename// +<tag/<tt/srorre// +<item> +Stacks the error handling behaviour currently in effect. Any changes +to error handling will take effect only between <prgn/errors-push/ and +<prgn/srorre/. + +<tag/<tt/catch-quit// +<tag/<tt/hctac// +<item> +Any use of <prgn/quit/ inside <prgn/catch-quit/ will merely cause the +parsing to continue at <prgn/hctac/ instead. Any control constructs +started since the <prgn/catch-quit/ will be considered finished if a +<prgn/quit/ is found. +<p> + +If an error occurs inside <prgn/catch-quit/ the execution settings +will be reset (as if by the <prgn/reset/ directive) and parsing will +likewise continue at <prgn/hctac/. +<p> + +If a lexical or syntax error is detected in the same configuration +file as the <prgn/catch-quit/, while looking for the <prgn/hctac/ +after an error or <prgn/quit/, that new error will not be caught. + +</taglist> + +<sect1 id="dirs-execution">Directives for changing execution settings +<p> + +The following directives modify the execution settings; the server +will remember the fact that the directive was encountered and act on +it only after all the configuration has been parsed. The <em/last/ +directive which modifies any particuar setting will take effect. + +<taglist> +<tag/<tt/reject// +<item> +Reject the request. <prgn/execute/, <prgn/execute-from-directory/ and +<prgn/execute-from-path/ will change this setting. + +<tag/<tt/execute <var/pathname/ [<var/argument/ ...]// +<item> +Execute the program <var/pathname/, with the arguments as specified, +followed by any arguments given to the client if +<prgn/no-suppress-args/ is in effect. It is an error for the +execution to fail when it is attempted (after all the configuration +has been parsed). If <var/pathname/ does not contain a slash it will +be searched for on the service user's path. + +<tag/<tt/execute-from-directory <var/pathname/ [<var/argument/ ...]// +<item> +Take all the characters after the last slash of the service name +specified when the client was called, and execute that program in the +directory named by <var/pathname/ as if it had been specified for +<var/execute/. The part of the service name used may contain only +alphanumerics and hyphens and must start with an alphanumeric (and it +must be non-empty), otherwise it is an error. +<p> + +This directive is ignored if the relevant program does not exist in +the directory specified; in this case the program to execute is left +at its previous setting (or unset, if it was not set before). +<p> + +It is an error for the test for the existence of the program to fail +other than with a `no such file or directory' indication. It is also +an error for the execution to fail if and when it is attempted (after +all the configuration has been parsed). + +<tag/<tt/execute-from-path// +<item> +<var/service/ is interpreted as a program on the default <prgn/PATH/ +(or as a pathname of an executable, if it contains a <tt>/</>). This +directive is <em/very dangerous/, and is only provided to make the +<prgn/--override/ options effective. It should not normally be used. +It is an error for the execution to fail when it is attempted (after +all the configuration has been parsed). + +<tag/<tt/execute-builtin <var/service-name/ <var/service-arguments// +<item> +Executes the builtin service <var/service-name/. These builtin +services display information about the server and/or the request, and +ignore any arguments passed from the service side except possibly to +print them as part of their output. They write their results to their +standard output (i.e., wherever file descriptor 1 is directed). The +builtin services are: + +<taglist compact> +<tag/<tt/execute// +<item> +Displays the execution settings, defined variables, +arguments, etc. with which the builtin service was invoked. + +<tag/<tt/environment// +<item> +Displays the environment variable settings with which the builtin +service was invoked. + +<tag/<tt/parameter <var/parameter/// +<item> +Displays the values of the service configuration language parameter +specified. + +<tag/<tt/version// +<item> +Displays the version string and compilation details of the uservd +server program. + +<tag/<tt/reset// +<item> +Displays the default reset configuration (evaluated when <prgn/reset/ +is found in a configuration file, or when an error is caught by +<prgn/catch-quit/). + +<tag/<tt/toplevel// +<item> +Displays the top-level default configuration (the configuration data, +evaluated by the server, which calls all the other configuration +files). + +<tag/<tt/override// +<item> +Displays the top-level override configuration (the configuration data, +evaluated by the server, which causes all the other configuration data +to be parsed). +</taglist> + +In the future other builtin services may be defined which do more than +just print information. + +<tag/<tt/set-environment// +<tag/<tt/no-set-environment// +<item> +Runs <tt>/etc/environment</> to set the service user's environment. +This adds the overhead of invoking a shell, but doesn't cause any +shell (de)mangling of the service's arguments. This is achieved by +invoking +<example> +.../program arg arg arg ... +</example> +as +<example> +/bin/sh -c '. /etc/environment; exec "$@"' - .../program arg arg arg ... +</example> +<prgn/no-set-environment/ cancels the effect of +<prgn/set-environment/. + +<tag/<tt/no-suppress-args// +<tag/<tt/suppress-args// +<item> +Include any arguments given to the client as arguments to the program +invoked as a result of an <prgn/execute/, +<prgn/execute-from-directory/ or <prgn/execute-from-path/ directive. +<prgn/suppress-args/ undoes the effect of <prgn/no-suppress-args/. + +<tag/<tt/require-fd <var/fd-range/ read|write// +<item> +Insist that the filedescriptor(s) be opened for reading resp. writing. +It is an error if any descriptor marked as required when the service +is about to be invoked (after the configuration has been parsed) was +not specified when the client was invoked. Each file descriptor has a +separate setting, and the last one of <prgn/require-fd/, +<prgn/allow-fd/, <prgn/ignore-fd/, <prgn/null-fd/ or <prgn/reject-fd/ +which affected a particular file descriptor will take effect. +<p> + +<var/fd-range/ may be a single number, two numbers separated by a +hyphen, or one number followed by a hyphen (indicating all descriptors +from that number onwards). It may also be one of the words +<tt/stdin/, <tt/stdout/ or <tt/stderr/. Open-ended file descriptor +rangers are allowed only with <prgn/reject-fd/ and <prgn/ignore-fd/, +as otherwise the service program would find itself with a very large +number of file descriptors open. +<p> + +When the configuration has been parsed, and before the service is +about to be executed, stderr (fd 2) must be required or allowed +(<prgn/require-fd/ or <prgn/allow-fd/) for writing; this is so that +the error message printed by the server's child process if it cannot +<prgn/exec/ the service program is not lost. + +<tag/<tt/allow-fd <var/fd-range/ [read|write]// +<item> +Allow the descriptor(s) to be opened for reading resp. writing, or +either if neither <tt/read/ nor <tt/write/ is specified. If a +particular descriptor not specified by the client then it will be open +onto <tt>/dev/null</> (for reading, writing, or both, depending on +whether <tt/read/, <tt/write/ or neither was specified). + +<tag/<tt/null-fd <var/fd-range/ [read|write]// +<item> +Specify that the descriptor(s) be opened onto <prgn>/dev/null</> for +reading resp. writing, or both if neither <tt/read/ nor <tt/write/ +is specified. Any specification of these file descriptors by the +client will be silently ignored; the client will see its ends of the +descriptors being closed immediately. + +<tag/<tt/reject-fd <var/fd-range/// +<item> +Do not allow the descriptor(s) to be specified by the client. It is +an error if any descriptor(s) marked for rejection are specified when +the service is about to be invoked (after the configuration has been +parsed). + +<tag/<tt/ignore-fd <var/fd-range/// +<item> +Silently ignore any specification by the client of those +descriptor(s). The pipes corresponding to these descriptors will be +closed just before the service is invoked. + +<tag/<tt/disconnect-hup// +<tag/<tt/no-disconnect-hup// +<item> +Causes the service's process group to get a <prgn/SIGHUP/ if the +client disconnects before the main service process terminates. +<prgn/no-disconnect-hup/ cancels <prgn/disconnect-hup/. +<p> + +If one of the reading descriptors specified when the client is called +gets a read error, or if the service is disconnected for some other +reason, then the <prgn/SIGHUP/ will be delivered <em/before/ the +writing end(s) of the service's reading pipe(s) are closed, so that +the client can distinguish disconnection from reading EOF on a pipe. + +<tag/<tt/reset// +<item> +Resets the execution settings to the default. This is equivalent to: +<example> +cd ~/ +reject +no-set-environment +suppress-args +allow-fd 0 read +allow-fd 1-2 write +reject-fd 3- +disconnect-hup +</example> + +</taglist> + +If no <prgn/execute/, <prgn/execute-from-path/, +<prgn/execute-from-directory/ or <prgn/builtin/ is interpreted before +all the files are read then the request is rejected. + + +<sect id="configerrors">Errors in the configuration file +<p> + +If a syntax error or other problem occurs when processing a +configuration file then a diagnostic will be issued, to wherever the +error messages are currently being sent (see the <prgn/errors-/ family +of directives, above). +<p> + +The error will cause processing of the configuration files to cease at +that point, unless the error was inside a <prgn/catch-quit/ construct. +In this case the settings controlling the program's execution will be +reset to the defaults as if a <prgn/reset/ directive had been issued, +and parsing continues after <prgn/hctac/. + + +<sect id="defaults">Defaults +<p> + +The default configuration processing is as if the daemon were parsing +an overall configuration file whose contents were as follows: + +<example> +reset +user-rcfile ~/.userv/rc +errors-to-stderr +include /etc/userv/system.default +if grep service-user-shell /etc/shells + errors-push + catch-quit + include-ifexist <var/file specified by most recent user-rcfile directive/ + hctac + srorre +fi +include /etc/userv/system.override +quit +</example> +<p> + +If one of the <prgn/--override/ options to the client is used then it +will instead be as if the daemon were parsing an overall configuration +as follows: + +<example> +reset +errors-to-stderr +include <var/file containing configuration data sent by client/ +quit +</example> + + +<chapt id="ipass">Information passed through the client/daemon combination +<p> + +The information described below is the only information which passes +between the caller and the service. + +<list> +<item> +The service name supplied by the caller is available in the +configuration language for deciding whether and which service program +to invoke, in the <prgn/service/ parameter, and is used by the +<prgn/execute-from-directory/ and <prgn/execute-from-path/ +configuration directives. It is usually used to select which service +program to invoke. It is also passed to the service program in the +<prgn/USERV_SERVICE/ environment variable. + +<item> +File descriptors specified by the client and allowed according to the +configuration language will be connected. Each file descriptor is +opened for reading or writing. Communication is via pipes, one end of +each pipe being open on the appropriate file descriptor in the service +program (when it is invoked) and the other end being held by the +client process, which will read and write files it opens on behalf of +its caller or file descriptors it is passed by its caller. +<p> + +Data may be passed into the service through reading pipes and out of +it through writing pipes. These pipes can remain open only until the +service and client have terminated, or can be made to stay open after +the client has terminated and (if the service program forks) the main +service process has exited; the behaviour is controlled by options +passed to the client by its caller. +<p> + +The caller can arrange that a writing pipe be connected to a pipe or +similar object and cause attempts to write to that descriptor by the +service to generate a <prgn/SIGPIPE/ (or <prgn/EPIPE/ if +<prgn/SIGPIPE/ is caught or ignored) in the service. +<p> + +Likewise, the service can close filedescriptors specified for reading, +which will cause the corresponding filedescriptors passed by the +caller to be closed, so that if these are pipes processes which write +to them will receive <prgn/SIGPIPE/ or <prgn/EPIPE/. + +<item> +If <prgn/no-suppress-args/ is set then arguments passed to the client +by its caller will be passed on, verbatim, to the service. + +<item> +Fatal signals and system call failures experienced by the client will +result in the disconnection of the service from the client and +possibly some of the communication file descriptors described above; +if <prgn/disconnect-hup/ is set then the service will also be sent a +<prgn/SIGHUP/. + +<item> +The value of the <prgn/LOGNAME/ (or <prgn/USER/) environment variable +as passed to the client will be used as the login name of the calling +user if the uid of the calling process matches the uid corresponding +to that login name. Otherwise the calling uid's password entry will +be used to determine the calling user's login name. +<p> + +This login name and the calling uid are available in the configuration +language in the <prgn/calling-user/ parameter and are passed to the +service program in environment variables <prgn/USERV_USER/ and +<prgn/USERV_UID/. +<p> + +The shell corresponding to that login name (according to the password +entry) is available as in the configuration language's +<prgn/calling-user-shell/ parameter. +<p> + +If no relevant password entry can be found then no service will be +invoked. + +<item> +The numeric values and textual names for calling gid and supplementary +group list are available in the configuration language in the +<prgn/calling-group/ parameter and are passed to the service in +environment variables. +<p> + +If no name can be found for a numeric group to which the calling +process belongs then no service will be invoked. + +<item> +The name of the current working directory in which the client was +invoked is passed, if available and not hidden using <prgn/--hidecwd/, +to the service program in the <prgn/USERV_CWD/ variable. This grants no +special access to that directory unless it is a subdirectory of a +directory which is executable (searchable) but not readable by the +service user. + +<item> +Settings specified by the caller using the <tt/--defvar +<var/name/=<var/value// option to the client are available in the +configuration language as the corresponding <tt/u-<var/name// +parameters and are passed to the service program in environment +variables <tt/USERV_U_<var/name//. + +<item> +If the calling user is root or the same as the service user then +options may be given to the client which bypass the usual security +features; in this case other information may pass between the caller +and the service. + +</list> + +<chapt id="notes">Applications and notes on use +<p> + +<sect id="reducepriv">Reducing the number of absolutely privileged subsystems +<p> + +Currently most Unix systems have many components which need to run as +root, even though most of their activity does not strictly require +it. This gives rise to a large and complex body of code which must be +trusted with the security of the system. +<p> + +Using <prgn/userv/ many of these subsystems no longer need any unusual +privilege. +<p> + +<prgn/cron/ and <prgn/at/, <prgn/lpr/ and the system's mail transfer +agent (<prgn/sendmail/, <prgn/smail/, <prgn/exim/ or the like) all +fall into this category. + +<sect id="noexcess">Do not give away excessive privilege to <prgn/userv/-using facilities +<p> + +There is a danger that people reimplementing the facilities I mention +above using <prgn/userv/ will discard much of the security benefit by +using a naive implementation technique. This will become clearer with +an example: +<p> + +Consider the <prgn/lpr/ program. In current systems this needs to +have an absolutely privileged component in order to support delayed +printing without copying: when the user queues a file to be printed +the filename is stored in the print queue, rather than a copy of it, +and the printer daemon accesses the file directly when it is ready to +print the job. In order that the user can print files which are not +world-readable the daemon is given root privilege so that it can open +the file in the context of the user, rather than its own. +<p> + +A simple-minded approach to converting this scheme to use <prgn/userv/ +might involve giving the printer daemon (the <prgn/lp/ user) the +ability to read the file by allowing them to run <prgn/cat/ (or a +special-purpose file-reading program) as any user. The <prgn/lpr/ +program would use a <prgn/userv/ service to store the filename in the +printer daemon's queues, and the daemon would read the file later when +it felt like it. +<p> + +However, this would allow the printer daemon to read any file on the +system, whether or not someone had asked for it to be printed. Since +many files will contain passwords and other security-critical +information this is nearly as bad as giving the daemon root access in +the first place. Any security holes in the print server which allow a +user to execute commands as the <prgn/lp/ user will give the user the +ability to read any file on the system. +<p> + +Instead, it is necessary to keep a record of which files the daemon +has been asked to print <em/outside/ the control of the print daemon. +This record could be kept by a new root-privileged component, but this +is not necessary: the record of which files a user has asked to be +printed can be kept under the control of the user in question. The +submission program <prgn/lpr/ will make a record in an area under the +user's control before communicating with the print server, and the +print server would be given the ability to run a special file-reading +program which would only allow files to be read which were listed in +the user's file of things they'd asked to print. +<p> + +Now security holes in most of the printing system do not critically +affect the security of the entire system: they only allow the attacker +to read and interfere with print jobs. Bugs in the programs run by the +print server to read users' files (and to remove entries from the list +of files when it has done with them) will still be serious, but this +program can be quite simple. +<p> + +Similar considerations apply to many <prgn/userv/-based versions of +facilities which currently run as root. +<p> + +It is debatable whether the user-controlled state should be kept in +the user's filespace (in dotfiles, say) or kept in a separate area set +aside for the purpose; however, using the user's home directory (and +probably creating a separate subdirectory of it as a dotfile to +contain many subsystems' state) has fewer implications for the rest of +the system and makes it entirely clear where the security boundaries +lie. + +<sect id="notreally"><prgn/userv/ is not a replacement for <prgn/really/ and <prgn/sudo/ +<p> + +<prgn/userv/ is not intended as a general-purpose system +administration tool with which system administrators can execute +privileged programs when they need to. It is unsuitable for this +purpose precisely because it enforces a strong separation between the +calling and the called program, which is undesirable in this context. +<p> + +Its facilities for restricting activities to running certain programs +may at first glance seem to provide similar functionality to +<prgn/sudo/<footnote><prgn/sudo/ is a program which allows users to +execute certain programs as root, according to configuration files +specified by the system administrator.</footnote>. However, the +separation mentioned above is a problem here too, particular for +interaction - it can be hard for a <prgn/userv/ service program to +interact with its real caller or the user in question. + +<sect id="nogeneral">Don't give access to general-purpose utilities +<p> + +Do not specify general purpose programs like <prgn/mv/ or <prgn/cat/ +in <prgn/execute-/ directives without careful thought about their +arguments, and certainly not if <prgn/no-suppress-args/ is specified. +If you do so it will give the caller much more privilige than you +probably intend. +<p> + +It is a shame that I have to say this here, but inexperienced +administrators have made similar mistakes with programs like +<prgn/sudo/. + +</book>