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.
LPRng provides native support for the LPR extensions used by MIT and the Kerberos 4 implementation. In addition, it provides Kerberos 5 based authentication.
LPRng also supports the use of the PGP (Pretty Good Privacy) program and can sign and optionally encrypt command and reponses between servers and clients.
Finally, LPRng provide a general purpose interface allowing users to insert their own authentication methods, either at the program level or at the code level.
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
REQ_VERBOSE 9 verbose status information
REQ_LPSTAT 10 lpstat simulation
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:
auth=
client to server authentication typeauth_client_filter=
client to server transfer programauth_forward=
server to server authentication typeauth_forward_filter=
server to server transfer programauth_forward_id=
Server identificationauth_receive_filter=
server to server transfer programauth_server_id=
server identificationThis section describes the general purpose interface used for client to server authentication.
The LPRng client will generate a set of commands and place them in a file. The file is then encrypted and/or signed by the appropriate authentication method, and is transferred to the server. The server will then decrypt and/or check the signature, perform the requested actions, and in turn generate a file of status information. This file is encrypted and/or signed by the server, and sent to the client, where it is in turn decrypted and/or checked for correct signature. These activities are controlled by the following printcap or configuration options.
auth
option specifies the authentication type to be used
for client to server transfers.
For example,
auth=pgp
would specify PGP authentication,
auth=kerberos5
would specify Kerberos 5 authentication,
auth=kerberos4
would specify Kerberos 4 authentication,
and
auth=user
would specify using user provided filters for authentication.auth_server_id
option.auth_forward
> option specifies the authentication type to be used
for server to server transfers.auth_server_id
.
and the remote server by
auth_forward_id
.getpwuid()
.The LPRng client will open a connection to the server and send a command with the following format:
\008printer C userid auth\n - for commands
\008printer C userid auth jobsize\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,
auth is the the value of the auth
option,
and jobsize is the size of the job file to be printed.
On reception of this command, the server will send a one byte success code. If an error is indicated by a non-zero response, additional error status may follow the non-zero success code byte. At the end of this information the connection will be terminated. The values used by LPRng are:
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,
client will use the auth_client_filter
to encrypt and/or sign
a data file to be transferred to the server.
The authentication program will have the following IO assignments:
FD Options Purpose
0 R/W connection to remote host
1 W output for returned status
2 W errors
Command line arguments:
program -C -Pprinter -nuser -Aauth -Rauth_server_id -Ttempfile
The 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.
The server will send the client authentication program any error or logging information over the FD 0 connection, in a form appropriate to the authentication operation. The client authenticator will write this information to FD 1. If data transfer or authentication fails, the authenticator will write an error message to FD 2 and exit with error code JFAIL.
If no error has occured the client authenticator will then exit with error code JSUCC.
Options used:
auth_receive_filter=
Server (lpd) authentication programauth_server_id=
Server identificationWhen an authentication command arrives at the server, it has the following form:
\008printer C userid auth\n - for commands
\008printer C userid auth jobsize\n - for print jobs
The server will attempt to find the printcap for the specified printer.
For some operations this printer will be a dummy entry;
this will simply cause the following operations to use the default
information in the lpd
configuration.
If a print job is being performed and the spool queue does not exist, then the job will be rejected. A non-zero error code will be written to the connection and the operation will terminate.
The auth
value is used to set the
AUTHTYPE permission checking value.
If the AUTHTYPE is not built in,
and the auth
value does not match the
printcap or configuration
auth
option value 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.
Many authentication programs require that the
users provide some form of key or identification.
The auth_server_id
option is used for this purpose.
The server will start the server authenticator program and provide the
following open file descriptors for it. The program
will run as the same UID as the 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 -Rauth_server_id -Ttempfile
The
PRINTER
,
USER
,
and
AUTHTYPE
,
are obtained from the original command.
The authentication filter will read the file transferred by the
client authenticator,
decrypt it,
and place the decrypted values in the tempfile.
It will then write
a
from_id
string to FD 1,
which will be read by the LPD server
and used as the identification of the originating end of the connection.
If the originating program is an LPRng client,
then the from_id
value will be the user identification for the authentication protocol;
if the originating program is an LPRng server,
this value will be the server identificatio for the authentication protocol.
After writing this value, the transfer program will close FD 1. At this point the LPD server will use the contents of the tempfile to perform the various requested actions.
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.
The lpd
server will record the
authentication information returned by the server in the
AUTHUSER permissions key.
The 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.
The 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.
At the end of the operations,
the FD 3 file descriptor will be closed and the lpd
server
will wait for the authentication process to exit.
The server authentication process will read input from FD 3 until the end of input, and then transfer the received information to the client side authenticator. It may use the tempfile to hold the information during the reading and transfer process.
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:
auth_forward=
Server to server authentication typeauth_forward_id=
Destination server authentication idThe 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.
The 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 procedure used to by the server to send commands and/or jobs is identical to that used by a client, with the minor modification that the server is identified as the originating endpoint of the connection, and the client authentication information is transferred in the file.
When propagating a command, the server uses the authentication information provided for the remote user by the client to server authentication program. When propagating or forwarding a job, the server will use the authentication information stored in the job control or hold file. This information will be represented as AUTHUSER in the following discussion.
The
auth_forward
option value specifies the type of authentication
to be used to forward authentication,
and the sending server uses the auth_server_id
as its identification,
and the auth_forward_id
as the identification of the remote server.
If there is no user authentication information,
then a normal, non-authenticated transfer will be done.
The auth_forward_filter
will be used for the forwarding
operation.
The sending server takes the part of the client, and will transfer a job acting similar to a client. The initial information transfer from the sending server will have the format:
\008printer F server_user authtype \n - for commands
\008printer F server_user authtype controlfilename\n - for print jobs
The sending server will invoke its authenticator with the arguments:
auth_forward_filter -F -Pprinter -nserver_user -aauthtype \
-Rremote_user -Ttempfile
The tempfile containing the job or command information to be sent will have the form:
user_authentication_info\n
\n
<normal file contents>
That is,
the user authentication information is place in the tempfile.
The tempfile will be transferred to the remote server in the same fashion
as for a user job.
Any error or logging information returned will either be written to the
lpd
log file or to the previous lpd
process in
the transfer chain.
On the destination server the same operations for receiving an authentication request from a client is performed. The AUTHUSER, AUTHFROM, and AUTHTYPE values will be the derived from the authentication request as for the client. The AUTHSAMEUSER will compare the remote client authentication information and the authentication information used to create the job.
When the remote server receives the authentication request, it will carry out the same actions as for a client to server transfer, modified as follows:
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.
This matches the type of authentication request.
Built in values include
kerberos4
,
kerberos5
,
and
pgp
.
The originating auth_user_id value.
When a command received from a server (i.e.- forwarded by a server), the value is the the forwarding servers authentication information, otherwise it is NULL.
The originating auth_user_id value is compared to the value used to create the job. If they are identical, the match succeeds.
For example, to reject non-authenticated operations, the following line could be put in the permissions file.
REJECT NOT AUTH
To reject server forwarded authentication as well, we use the following.
Note that the ?
forces a value to be present.
REJECT AUTH AUTHFROM=?*
If a remote server has id information FFEDBEEFDEAF, then the following will accept only forwarded jobs from this server. Note that AUTHFROM will only match on authenticated transfers; FWDUSER will only match on forwarded transfers.
ACCEPT AUTH AUTHFROM=FFEDBEEFDEAF
REJECT AUTH
REJECT NOT AUTH
To allow only authenticated users to remove jobs you can use:
ACCEPT AUTH SERVICE=R,M,L,P AUTHSAMEUSER
REJECT AUTH
REJECT NOT AUTH
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.
LPRng has greatly simplified the use of PGP for authentication by building in support as follows.
The LPD server usually runs as user daemon
,
and opens files as
daemon
.
The system administrator should establish a home directory for daemon,
and use the PGP key generation facility to create a public and private key
for the daemon
user.
By default,
the PGP program puts the public and secret key rings in the
$HOME/.pgp/
directory,
and sets them to be readable only by the user.
You should log in temporarily as
daemon
,
run the
pgp -kg
command,
and then disable logins for daemon
.
The user id chosen for the LPD server should be easily used to identify
the server.
For example,
lpr@hostname
,
where hostname is the fully qualified domain name of the server is userful.
The next step is to place the passphrase in a file that is only readable by
daemon
,
say
~daemon/.pgp/serverkey
,
with owned by daemon
,
and with 600
permissions (read/write only by daemon
).
This is extremely important,
as if any other users can read this file then security will be severely compromised.
The next step is to distribute the lpr@hostname
public key to all users of the
LPRng server,
and to place the public keys of LPRng users in the daemon
public key ring.
This can be done using:
pgp -kxa userid destfile keyfile
Example:
> pgp -kxa lpr@astart /tmp/lprkey ~daemon/.pgp/pubring.pgp
Key for user ID: lpr@astart
512-bit key, key ID BB261B89, created 1999/01/01
Transport armor file: /tmp/lprkey.asc
Key extracted to file '/tmp/lprkey.asc'.
User can add the lpr@astart
key to their public key rings using:
pgp -ka /tmp/lprkey.asc
Finally, the administrator will need to add users public keys to the
daemon
users public key ring. This can most easily be done by
copying all the keys (in ASCII text form) to a single file
(/tmp/keyfile
)and using:
pgp -ka /tmp/keyfile ~daemon/.pgp/pubring.pgp
Options used:
pgp_path=
path to PGP programpgp_server_key=
path to server passphrase filepgp_passphrase=
user passphrase file in PGPPATH or $HOME/.pgpThe pgp_path
option is the path to the PGP program.
The pgp_server_key
is the path to the file containing the server passphrase.
This file will be read by lpd
to get the passphrase to unlock the server's
keyring.
The LPRng client software will check to see if the
pgp_passphrase
value (default clientkey
) file in
$PGPPATH
, and if not present,
then in $HOME/.pgp
.
By default, the pass_env
option is pass_env=PGPPASS,PGPPATH,PGPPASSFD
and will pass the values of the PGPPATH, PGPPASS,
and PGPPASSFD environment variables. See below for a method to use these.
Example printcap entry:
pr:
:lp=pr@wayoff
:auth=pgp
:auth_server_id=lpr@wayoff.com
:pgp_path=/usr/local/bin/pgp
:pgp_passphrase=.mypass
One problem with using PGP is the need to have users input their
passphrases.
If the user is daring, then the pass phrase can be put in the file:
~/.pgp/clientkey
. This file will be read by the
LPRng client program the contents passed to PGP as the passphrase.
This file MUST have 0400 permissions (read only by user)
and MUST owned by the user.
A more subtle solution is to use the PGPPASSFD
environment variable
facility.
This causes PGP to read the passphrase from a file descriptor.
If the user puts his passphrase in a file, say
$(HOME)/.pgp/.hidden
,
then the following shell script can be used.
#!/bin/sh
# /usr/local/bin/pgplpr script - passphrase in $(HOME)/.pgp/.hidden
#
PGPASSFD=3 3<$(HOME)/.pgp/.hidden lpr "$@"
If you wish to enforce the use of authentication, then you should modify the lpd.perms file. Here are some examples.
# force authentication
REJECT NO AUTH
REJECT NO AUTHTYPE=pgp
One problem with using PGP is the need to have users input their
passphrases.
If the user is daring, then the pass phrase can be put in the file:
~/.pgp/clientkey
. This file will be read by the
LPRng client program the contents passed to PGP as the passphrase.
This file MUST have 0400 permissions (read only by user)
and MUST owned by the user.
A more subtle solution is to use the PGPPASSFD
environment variable
facility.
This causes PGP to read the passphrase from a file descriptor.
If the user puts his passphrase in a file, say
$(HOME)/.pgp/.hidden
,
then the following shell script can be used.
#!/bin/sh
# /usr/local/bin/pgplpr script - passphrase in $(HOME)/.pgp/.hidden
#
PGPASSFD=3 3<$(HOME)/.pgp/.hidden lpr "$@"
By using the -V (verbose) flag, users can see the results of the PGP interaction.
LPRng Kerberos 5 authentication is based on the Kerberos5-1.0.5 release as of March 28, 1999. 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
lpr/hostname.REALM
as a template-
i.e.
lpr/astart1.astart.com@ASTART.COM
for an example.
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
/etc/lpd.conf
:
auth=kerberos5
kerberos_keytab=/etc/lpd.keytab
kerberos_service=lpr
# optional - explicit prinipal name for server, used by clients
#kerberos_server_principal=lpr/hostname@REALM
# optional - forwarding to another server with authentication
# kerberos_forward_principal=lpr/hostname@REALM
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
used in the above key generation 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
:auth=kerberos5
:auth_server_id=lpr/erehwon.org@BLUESKY.ORG
LPRng has built-in support for the Project Athena extensions to the RFC1179 protocol. These provide an extremely simple authentication protocol using an initial credential exchange. After the initial exchange the usual RFC1179 protocol is used.
To enable Kerberos 4 support,
you must modify the LPRng/src/Makefile
and recompile
the LPRng code.
You should be aware that this is not a supported extension,
and is provided as a courtesy to MIT and Project Athena.