One of the major problems in a print spooler system is providing privacy and authentication services for users. One method is to construct a specific set of protocols which will be used for providing the privacy or authentication; another is to provide a simple interface to a set of tools that will do the authentication and/or encryption. The LPRng system adopts this latter approach.
A careful study of the authentication problem shows that it should be done during reception of commands and/or jobs from a remote user and/or spooler. At this time the following must be done:
When a user logs into a system, they are assigned a user name and a corresponding UserID. This user name is used by the LPRng software when transferring jobs to identify the user.
When we look into the problem of authentication, we will possibly have a more global user identification to deal with, the authentication identifier (AuthID). One way to deal with this problem is to give LPRng intimate knowledge of the UserID and AuthID relationship. While this is possible, it may be difficult to deal with in a simple and extensible manner. An alternate solution is to provide a mapping service, where the authentication procedure provides a map between the UserID and AuthID.
The RFC1179 protocol specifies that a LPD server command sent on a connection has the form:
\nnn[additional fields]\n
\nnn
is a one octet (byte) value with the following meaning:
REQ_START 1 start printer REQ_RECV 2 transfer a printer job REQ_DSHORT 3 print short form of queue status REQ_DLONG 4 print long form of queue status REQ_REMOVE 5 remove jobs
The LPRng system extends the protocol with the following additional types:
REQ_CONTROL 6 do control operation REQ_BLOCK 7 transfer a block format print job REQ_SECURE 8 do operation with authentication
The REQ_CONTROL allows a remote user to send LPC commands to the server. The REQ_BLOCK provides an alternate method to transfer a job. Rather than transferring the control and data files individually, this format transfers one file. The REQ_AUTH provides a mechanism for providing an authentication mechanism and is described in this document.
Options used:
use_auth=
Authentication typedefault_auth=
Default authentication typeremote_user=
Remote server identification server_user=
Server identificationuser_auth_command=
User to server authentication program use_auth
option is checked to see if it specifies
an authentication type.
If it does, then the use_auth
value is used to identify the
type of authentication to be done.lpr
, lpq
,
etc program,
the user specifies that an authenticated transfer is to be done
and the use_auth
value is not set,
then use_auth
is set to the default_auth
value.use_auth
has a value,
then an authenticated transfer will be done.remote_user
and
server_user
printcap option values.
If the remote_user
value is not specified, then
it will be set to server_user
(default daemon
).
As discussed below,
the server_user
is usually used to identify the
lpd
server on an originating host,
but a general form of identification can be used.userid
is the value obtained by using the current UID
of the program as the search value for getpwuid()
.\008printer C userid use_auth\n - for commands \008printer C userid use_auth controlfilename\n - for print jobs
Note that \008 is a one byte code indicating an authenticated transfer. Printer is the spool queue name, C in the character 'C' indicating a client request, userid is the login id of the user, use_auth is a keyword identifying the authentication type, and controlfile name is the name of a controlfile to be transferred.
ACK_SUCCESS 0 success ACK_STOP_Q 1 failed; no spooling to the remote queue ACK_RETRY 2 failed; retry later ACK_FAIL 3 failed; job rejected, no retry
If the success code is zero, the sender or client will start the
client side authentication program specified by the
user_auth_command
option.
This program will be passed the same options as normal filter
program.
See
Filters for details.
The authentication program will have the following IO assignments:
FD Options Purpose 0 R/W socket connection to remote host (R/W) 1 W pipe or file descriptor, for responses to client 2 W error log
Command line arguments:
program -C -Pprinter -nuser -asend_auth -Rremote_user -Ttempfile
The authenticator program can create additional temporary or working
files with the pathnames tempfile.ext
; these will be deleted
after the authentication process has been completed.
The command line arguments will undergo replacement of %H and %R
values. This means that when authenticating to host nowhere.com,
a remote_user=lpr@%R
will be expanded to
remote_user=lpr@
nowhere.com.
kerberos
authentication.
The same actions are carried out. tempfile
will contain either a command line as would be
transferred using the standard RFC1179 protocol,
or a print job in block format.
See
RFC1179 Protocol for details.
The client authenticator program will open and transfer
the contents of tempfile
to the server authenticator,
using FD 0
and a format compatible with the underlying authentication mechanism.
If the transfer fails the client authenticator will log error information on FD 2 and then exit with error code JFAIL.
Options used:
server_auth_command=
Server (lpd) authentication program\008printer C userid use_auth\n - for commands \008printer C userid use_auth controlfilename\n - for print jobs
The server will decode the various fields, and set the following permission keys:
PRINTER=printer
USER=user
AUTHTYPE=use_auth
AUTH=USER
lpd
configuration.server_auth_command
value is not specified,
then authentication will fail.
An error message will be logged to the server log file, and a non-zero error code and message will be written to the connection to the remote client program.
server_user
option is used for this purpose.
If there is no value specified,
server_user
is set to the user
(default daemon
) option value.lpd
server.
If this is a print job transfer,
the current directory will be the spool directory of the print queue.
FD Options Purpose 0 R/W socket connection to remote host (R/W) 1 W pipe or file descriptor, for information for server 2 W error log 3 R pipe or file descriptor, for responses to client
Command line arguments:
program -S -PPRINTER -nUSER -aAUTHTYPE -Rserver_user -Ttempfile
The authenticator can create additional temporary or working files with the pathnames tempfile.ext; these will be deleted after the authentication process has been completed.
tempfile
.
In order for the LPRng software to perform permissions checking, it needs an authenticated identifier for the user. The server authenticator will write such an identifier to FD 1. This should be a single line, of a maximum of 127 characters (LPRng LINEBUFFER maximum size), i.e.:
authentication_info\n
If the transfer step or authentication fails, then the server authenticator will write an error message to FD 2 and exit with error code JFAIL.
lpd
server will record the
authentication information returned by the server in the
AUTHUSER permissions key.
If a print job is being transferred, it will also be stored in the control file. This allows job forwarding as is discussed below.
lpd
server will perform the usual permissions checks,
with the addition of the indicated permission keys and associated values.
During this process,
any error messages or logging information normally returned to client
programs will be written to the authentication program FD 3.lpd
server will carry out either the commands or
print job specified in the temporary file.
During this process,
any error messages or logging information normally returned to client
programs will be written to the authentication program FD 3.lpd
server
will wait for the authentication process to exit.If the transfer of the logging information fails, then the authenticator process will exit with error code JFAIL, otherwise it will exit with error code JSUCC.
Options used:
forward_auth=
Server forwarding authentication typeThe Server to Server authentication procedure is used by one server to forward jobs or commands to another server. It should be noted that this forwarding operation puts an implicit trust in the security of the client to server to server chain.
lpd
server will perform an authenticated transfer to
another server when it either needs to transfer a job to a remote printer
or when it needs to propagate a
lpq
,
lprm
,
or
lprc
operation.
The
<tt>remote_support</tt>
command can be used to control this forwarding
operation.server_user
value is used.
If it is not specified, the server_user
value is set to the
user
(default daemon
) value.
For the remote server,
the remote_user
value is used.
If it is not specified, the remote_user
value is set to the
user
(default daemon
) value.server_auth_command
will be used for the forwarding
operation.
If it does not have a value,
then a normal, non-authenticated transfer will be done.\008printer F server_user authtype \n - for commands \008printer F server_user authtype controlfilename\n - for print jobs
server_auth_command -F -Pprinter -nserver_user -aauthtype \ -Rremote_user -Ttempfile
user_authentication\n <normal file contents>That is, the user authentication information is place in the tempfile.
lpd
log file or to the previous lpd
process in
the transfer chain.server_user
and authtype
fields in the command sent to the server.
The server_user
value is obtained as for client to server
transfers.
The server side authenticator is invoked with the arguments:
server_auth_command -S -Pprinter -nUSER -aAUTHTYPE \ -Rremote_user -Ttempfile
lpd
server will remove the first line of the transferred file,
which contains the user authentication information,
and set
AUTHUSER to this value.The following patterns and values can be used to check that a particular type of authentication has been used, and what the authenticated user information is.
AUTH=NONE - no authentication AUTH=USER - authentication from a client AUTH=FWD - forwarded authentication from a lpd server
For a command received from a client, the value is auth_user information returned by the authenticator
For a command received from a server (i.e.- forwarded by a server), the value is the original auth_user_id determined by the remote server.
For a command received from a server (i.e.- forwarded by a server), the value is the the forwarding servers authentication information.
For example, to reject non-authenticated operations, the following line could be put in the permissions file.
REJECT AUTH=NONE
To reject server forwarded authentication as well, we use:
REJECT AUTH=NONE,FWD
If a remote server has id information FFEDBEEFDEAF, then the following will accept only forwarded jobs from this server. Note that AUTHUSER will only match on authenticated transfers; FWDUSER will only match on forwarded transfers.
ACCEPT FWDUSER=FFEDBEEFDEAF REJECT AUTH=FWD
PGP is a well known encryption and authentication program. For more details see the web site http://www.pgp.net or the ftp site ftp://ftp.pgp.net.
The
LPRng/src/AUTHENTICATOR/authenticate_pgp.sh
program uses the standard pgp
program and
facilities to do authentication.
It uses the following pgp
key organization.
User Name <userid@host> <userid1@host> ...
userid
must be the userid used for submitting jobs.
Note that that different userids can be put
into the key string as long as there is one entry for
each userid that the user has.
For example, suppose that user John Smith has several accounts on different hosts: john, jsmith, jsmith1. Then his key should have in it:
John Smith <john@whereever> <jsmith@nowhere> <jsmith1@whatever> ...
If the user is daring, then the pass phrase can be put in the file:
~/.pgp/clientkey
. This file will be read by the
authenticate_pgp
script and the contents passed to
PGP as the key.
This file MUST have 0400 permissions (read only by user) and MUST owned by the user. This is very dangerous; but so is setting the PGPPASS environment variable.
~daemon/.pgp
directory, i.e. - the default used by PGP.
The daemon public key identification should have the format:
daemon_hostname daemon <daemon_hostname@hostname>
In addition, the pass phrase for the daemon user must be
put in the file
~daemon/.pgp/serverkey
.
Note: you may have one daemon user with the same key for ALL servers, or you may have different ones for different servers.
By default, the
authenticate_pgp
script and several helper
programs, readfilecount
and removeoneline
,
will be installed in the standard executable locations.
You must set the following printcap and/or configuration variable to on. Note that the printcap keys override the configuration keys.
user_auth_command=/usr/local/.../authenticate_pgp use_auth=pgp server_auth=pgp server_user=daemon_id_for_server pass_env=PGPPASS,PGPPATH
Example printcap entry:
pr: :lp=pr@wayoff :use_auth=pgp :server_auth=pgp :server_user=daemon_wayoff
If you wish to enforce the use of authentication, then you should modify the lpd.perms file. Here are some examples.
# force authentication REJECT AUTH=NONE # in addition to above, # do not accept forwarded authentication REJECT AUTH=FWD # if the above is too strong, you can # reject forwarded authentication unless from specified server # note: U1, U2, etc. are the userids of the remote server REJECT AUTH=FWD NOT FWDUSER=U1@*,U2@*,U3@* # you can be paranoid and also check to see that that host # agrees with the userid reported. REJECT AUTH=FWD FWDUSER=U1@host1
The user should either have the PGPPASS environment variable defined, or have in his home directory the file /.pgp/clientkey. This file will be read by the script and used for the value of the PGPPASS environment variable. If neither of these is available, then the pgp program will interactively request this information.
pgp -kg User ID: daemon_test <daemon_test@host> Pass phrase: daemon_test pgp -kg User ID: user_test <user_test@host> Pass phrase: user_test
echo user_test >~/.pgp/serverkey echo user_test >~/.pgp/clientkey
sh authenticate_pgp -D -nuser_test -Rdaemon_test
The output should resemble:
server PGPPASS daemon_test ORIGKEY PRaTACFJGcQV92TE6bX72W2JHNNGPRIR7 SERVER AUTH TRANSFERFILE temp.str 398 -----BEGIN PGP MESSAGE----- Version: 2.6.3i ... -----END PGP MESSAGE----- client PGPPASS user_test
If there is an error, an error message will be printed.
The next test will make sure that the daemon user can access its public and secret keyring when running as user daemon.
pgp -kxa userid userid_public_key
deamon
with a home directory
that is owned and readable only by
daemon
.
You do not need a password (logon) capabilities for
daemon
.
Create the daemon/.pgp directory, and make sure that it has 0700 permissions.
daemon
.
The SU command does not update the $HOME environment variable.
Do the following to set $HOME:
export HOME=`eval echo ~daemon`
host
should be the host name for this server).
Set the pass phrase to a suitable value.
Make a note of these values - you will need them later.
pgp -kg User ID: daemon_host <daemon_host@host> Pass phrase: ....
pgp -kxa daemon_host daemon_public_key
userid_public_key
was created previously.
As daemon do:
pgp -ka userid_public_key
pgp -ka daemon_public_key (from step 4.5 above).
echo hi >/tmp/msg pgp -seat msg daemon_host -u userid -o /tmp/msg.pgp chmod 777 /tmp/msg.pgp
As daemon, check to see if you can decode this message.
pgp /tmp/msg.pgp -o /tmp/msg.dec diff /tmp/msg /tmp/msg.dec
pgp -seat /tmp/msg.dec userid -o /tmp/msg.pgp -u daemon_host chmod 777 /tmp/msg.pgp
As user, decode the message for user.
pgp /tmp/msg.pgp -o /tmp/msg.dec2 diff /tmp/msg.dec2 /tmp/msg
This test will check the actual authentication process carried
out by
authenticate_pgp
.
setupauth
program
and install it in a temporary location.
Note that
setupauth
is not
an installed part of the LPRng distribution, but is a
accessory.
cd LPRng/src make setupauth mv setupauth /tmp/setupauth
setupauth
has the command line:
setupauth clientid 'client command' serverid 'servercommand'
For example:
/tmp/setupauth \ papowell "/bin/ksh -c 'echo CLIENT; printenv ;'" \ papowell "/bin/ksh -c 'echo SERVER; printenv ;'"
This would display the environment variables set up by the setupauth program. This are similar to those used by LPRng.
#!/bin/sh user=${USER:-`whoami`} # show environment variables #/tmp/setupauth \ # "${user}" "/bin/ksh -c 'echo CLIENT; printenv ;'" \ # "${user}" "/bin/ksh -c 'echo SERVER; printenv ;'" # #exit 0; # # check bidirectionality of data transfer # /tmp/setupauth \ # "${user}" "/bin/ksh -c 'echo CLIENT STARTING SENDING 1>&2; echo hi 1>&0;'" \ # "${user}" "/bin/ksh -c 'echo SERVER READING; cat ;'" \ # sleep 3; # /tmp/setupauth \ # "${user}" "/bin/ksh -c 'echo CLIENT STARTING READING; cat '" \ # "${user}" "/bin/ksh -c 'echo SERVER SENDING 1>&2; echo hi 1>&0'" # # check the authenticate pgp operation # Note: you can also use # 'sh -x ./authenticate_pcp ...' to see the detailed actions. # Note: you must be root to run the next test. # rm /tmp/tempc /tmp/temps echo Hi $$ >/tmp/tempc chmod 777 /tmp/tempc /tmp/setupauth \ "${user}" './authenticate_pgp -C -n"${user}" -R"${user}" -T/tmp/tempc' \ "${user}" './authenticate_pgp -S -n"${user}" -R"${user}" -T/tmp/temps' rm /tmp/tempc /tmp/temps echo Hi $$ >/tmp/tempc chmod 777 /tmp/tempc /tmp/setupauth \ "${user}" './authenticate_pgp -C -n"${user}" -Rdaemon -T/tmp/tempc' \ daemon './authenticate_pgp -S -n"${user}" -Rdaemon -T/tmp/temps'
testauth
needs to run as root to change userids.
As root
,
Set the USER environment variable to the user you desire to simulate and
execute the testauth script.
USER=myself /tmp/testauth
:user_auth=pgp :user_authentication_command=/usr/local/lib/authenticate_pgp :server_auth=pgp :server_authentication_command=/usr/local/lib/authenticate_pgp
You can distribute the daemon PGP public key fairly easily - see the PGP documentation for key server information.
Each daemon must have the user's PGP key installed, or must in some way get the key from a trusted server. This is very site dependent and needs to be done by each site administrator.
The LPRng use of Kerberos authentication was based on the Kerberos5-1.0 release as of December 20, 1996. This was obtained from MIT:
Note that the distribution has only the most superficial documentation. There are no man pages for any of the support libraries, etc. etc.
if [ -f /etc/krb5.conf -a -f /usr/local/var/krb5kdc/kdc.conf ]; then echo -n ' krb5kdc '; /usr/local/sbin/krb5kdc; echo -n ' kadmind '; /usr/local/sbin/kadmind; fi
Do this for all the servers. You should use fully qualified domain names for the principals.
kadmin ... ktadd -k file_for_host lpr/hostname.REALM
The 'file_for_host' contains the keytab information, which is the equivalent information for the server.
/etc/lpd.keytab
.
Make sure that this file is readable only by user daemon
,
as it will try to read the file to get its server key.
#> ls -l /etc/lpd.keytab -rw------- 1 daemon wheel 128 Jan 16 11:06 /etc/lpd.keytab
use_auth=kerberos default_auth=kerberos kerberos_keytab=/etc/lpd.keytab kerberos_service=lpr kerberos_life= kerberos_renew=
The kerberos_keytab entry is the location of the keytab file; kerberos_service is the service that will be used to generate a server principal name. This is the "lpr" that appears in the above operations.
kerberos_life and kerberos_renew determine the lifetime and renewability of Kerberos tickets. The lifetime defaults to 10 hours, and the ticket will be refreshed when it expires if necessary.
cd LPRng/src; make sserver sclient usage: sserver [-D] [-p port] [-s service] [-S keytab] file -D turns debugging on 1. opens TCP port 'port' (default 1234) 2. waits for a connection 3. when a connection comes in, uses 'service' to get the principal name of the server, and looks up the key in keytab file. 4. Goes through the kerberos authentication. 5. Copies the input from remote server to 'file' 6. exits. usage: sclient [-D] [-p port] [-s service] host file -D turns debugging on 1. opens a connection to port on host (i.e. - host%port) 2. does the authentication. You must have done kinit to get for your ticket to be valid. 3. sends the file to remote host.
To test this, start up sserver on one host/window, then run sclient. The error messages are pretty straight forward, and when in doubt, look at the source code which has more than sufficient information.
Restart the server, and then try getting information using LPQ.
You can turn on tracing at LPQ to see if authentication is being used and is working:
lpq -Dnetwork,database
If the lpq works, then try send a job and see if the transfer is successful.
If you are using printers in different domains, then you can put the explicit principal name of the server in the printcap file, using the server_principal entry. For example:
lp_offsite :lp=printer@erehwon.org :use_auth=kerberos :kerberos_server_principal=lpr/erehwon.org@BLUESKY.ORG