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 MIT Kerberos 4 extensions and Kerberos 5 authentication.
LPRng has native support for the PGP (Pretty Good Privacy) program and can sign and optionally encrypt command and responses between servers and clients. Due to legal restrictions, an external PGP program must be used for this purpose.
A simple MD5 hash based authentication scheme is also provided as an example to illustrate how new or different authentication methods can be adddd.
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 is 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:
auth=
client to server authentication typeauth_forward=
server to server authentication typeXX_id=
server identificationXX_forward_id=
Server identificationA client (lpr, lpc, etc
to lpd
server authenticated transfer
proceeds as follows.
If an authenticated transfer is specified by the
auth=protocol
entry in the printcap or configuration information,
the client sends a request for an authenticated transfer
to the server.
Part of the authentication request is the authentication type.
If authentication type XX
is requested
the server will examine the information in the printcap and configuration
entries for an XX_id
value.
If this value is present then the server supports authentication of this type.
Further permission checks are carried out and finally the
server will accept or reject the authentication request.
If the request is accepted the server returns a postive
acknowlegement (single 0 byte) to the requester,
otherwise it returns a nonzero value and an error message.
If the request is accepted then an authentication specific protocol exchange is carried out between client and server. The commands and/or data files are encrypted and/or signed and transferred to the server. The protocol specific software on the server will then decrypt and/or check signatures, perform the requested actions, and in turn generate a status information. The status information is encrypted and/or signed by the server and sent to the client, where the client decrypts and/or checked for correct signature.
A lpd
server to lpd
server authenticated transfer
proceeds as follows.
If an authenticated transfer is specified by the
auth_forward=protocol
entry in the printcap or configuration information,
the originating server sends a request for an authenticated transfer
to the destination server.
The originating server plays the part of the client
and performs the same set of actions.
The following printcap or user level information needs to be provided for an authenticated exchange.
auth
option specifies the authentication type to be used
for client to server transfers.
For example,
auth=kerberos5
would specify Kerberos 5 authentication,
auth=kerberos4
would specify Kerberos 4 authentication,
auth=pgp
would specify PGP authentication,
auth=md5
would specify MD5 authentication,
etc.
The special form auth@
specifies no authentication.auth_forward
option specifies the authentication type to be used
for server to server transfers.
For example,
auth_forward=kerberos5
would specify Kerberos 5 authentication,
etc.
The special form auth@
specifies no authentication.
\008printer C user_id authtype \n - for commands (lpq, lpc, etc.)
\008printer C user_id authtype size\n - for print jobs (lpr)
\008printer F server_id authtype \n - forwarded commands (lpq, lpc, etc.)
\008printer F server_id authtype size\n - forwarded print jobs (lpr)
The single character with the \008
value signals that this
is an authentication request
the printer
is the name of a print queue,
and the C
(client) or F
indicates that the request is from
a client program or is a forwarded request from a server.
The user_id
or server_id
field is an identifier supplied by
the originator and is dicussed below.
If the size
value is present then the request
is for a job transfer and this value represents the job size.
It is used to determine if there is sufficient space in the spool queue
for the job.
user_id
or server_id
fields in the authentication
request are obtained as follows.
If the request originates from a client,
then the user_id
is the user name of the originator obtained from
password information.
If the request originates from a server,
then the server_id
is the printcap or configuration
xx_id=server_id
value,
where xx
is the value of the auth_forward=xx
entry.AUTHFROM
and AUTHUSER
strings will be supplied
to the lpd server for purposes of permission checking.When an authenticated transfer has been performed, the following permission information will be provided.
true
or match
if an authenticated request
was received.authtype
field in the authentication
request.AUTHUSER
information provided by the authentication
protocol,
and is usually the originating user's identification.AUTHUSER
information provided by the authentication
protocol,
and is usually the originating system (user or lpd server) identification.AUTHUSER
information from the request is compared to the
AUTHUSER
information from the request that created a 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
If a remote server has id information FFEDBEEFDEAF, then the following will accept only forwarded jobs from this server.
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.
user
and group
configuration entry (defaults
daemon
and daemon
respectively) specify the user and group id
used by the lpd
server for file and program execution.
PGP uses the current user id of the PGP process to determine the locations
of various configuration files and information.
In this discussion we will assume that lpd
runs as uid daemon
.$HOME/.pgp/
directory
to be readable only by the user.
In order to set up PGP authentication,
make sure that the daemon
account has a home directory.
Then use the su daemon
command to change effective UID to daemon
and run the
pgp -kg
(generate key)
command as daemon.
The daemon
user should not have a password.lpd
key be lpr@hostname
,
where hostname is the fully qualified domain name of the server.
A public and a private key file will be created.daemon
user in
~daemon/.pgp/serverkey
,
and make sure it has owner daemon
and 600
permissions (read/write only by daemon
).
This is extremely important.
If other users can read this file then security will be severely compromised.lpr@hostname
public key to all users of the
LPRng server.
This is usually done by placing the public key in a well known file location
or making it available to users by some form of Public Key Distribution system
(PKD).
The key can be extracted and put into a text file using the following commands:
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'.
daemon
public key ring.
This can be done using:
pgp -ka /tmp/lprkey.asc
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:
su daemon
pgp -ka /tmp/keyfile ~daemon/.pgp/pubring.pgp
lpd
server is using PGP to forward jobs or requests,
the destination server's public key must be put in the originating
servers public keyring. For example:
su daemon
pgp -ka /tmp/lpd.keyfile ~daemon/.pgp/pubring.pgp
Options used:
pgp_path=
path to PGP programpgp_id=
destination server key used by clientspgp_forward_id=
destination server used by serverpgp_server_key=
path to server passphrase fileExample printcap entry:
pr:
:lp=pr@wayoff
:auth=pgp
:pgp_id=lpr@wayoff.com
:pgp_path=/usr/local/bin/pgp
pr:server
:lp=pr@faroff
:auth_forward=pgp
:pgp_id=lpr@wayoff.com
:pgp_path=/usr/bin/pgp
:pgp_forward_id=lpr@faroff.com
The pgp_path
value is the path to the PGP program.
The progam must be executable by all users.
The pgp_id
value is the id used by PGP to look extract keys from
key rings.
When doing a client to server transfer this will be supplied as the id
to be used for the destination,
and the user's public keyring will be checked for a key corresponding to
this id.
When a request arrives at the server,
the server will use this value as the id of a key in its private key ring.
Finally,
when a server is forwarding a request to a remote server,
it will use this value
as the id of the key in its private key ring to be used to sign
or encode the destination information.
The pgp_forward_id
value is used by the lpd
server as the id
to use to find a key for the destination.
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.
Options used:
PGPPASSFILE=
File to read PGP passphrase fromPGPPASSFD=
File descriptor to read PGP passphrase fromPGPPASS=
PGP passphraseOne problem with using PGP is the need to have users input their passphrases. The following methods can be used.
$(HOME)/.pgp/.hidden
,
and set the PGPPASSFILE
environment variable to the file name.
This file will be opened and read by PGP to get the passphrase.
This file should be owned by the user and have 0600
or read/write
only by user permissions.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 "$@"
PGPPASS
environment variable.
Since the ps
command can be used to list the environment variables
of processes,
this is highly undesireable and should not be used under any circumstances.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.
The following sections describe how to set up and test the Kerberos software, and then how to configure LPRng to use Kerberos.
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.
kadmin ...
ktadd -k file_for_host lpr/hostname.REALM
/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
Options used:
auth=kerberos5=
use Kerberos5 authenticationkerberos_id=
destination server key used by clientskerberos_server_principal=
alias for kerberos_idkerberos_forward_id=
destination server used by serverkerberos_forward_principal=
alias for kerberos_forward_idkerberos_keytab=
location of the lpd server keytab filekerberos_service=
service to be usedkerberos_life=
lpd server ticket lifetimekerberos_renew=
lpd server ticket renewExample printcap entry:
pr:
:lp=pr@wayoff
:auth=kerberos5
:kerberos_id=lpr@wayoff.ASTART.COM
pr:server
:lp=pr@faroff
:auth_forward=kerberos5
:kerberos_id=lpr@wayoff.ASTART.COM
:kerberos_forward_id=lpr@faroff.ASTART.COM
:kerberos_keytab=/etc/krb5.keytab
The printcap configuration for Kerberos authentication is very simple.
The kerberos_id
is the principal name of the lpd server
that clients will connect to.
For backwards compatibility,
kerberos_server_principal
can also be used.
This values is used to obtain a ticket for the lpd
server,
and is the only entry required for client to server authentication.
The other entries are used by the lpd
server.
kerberos_keytab
entry is the location of the keytab file to be used by the server.
This contains the passphrase used by the server to authenticate itself
and get a ticket from the ticket server.
The kerberos_id
value is also used by the server during the
authentication process to make sure that the correct principal name
was used by the request originator.
This check has saved many hours of pain in trying to determine why
authentication is failing.
The
kerberos_life
and kerberos_renew
set the lifetime and renewability
of the lpd server Kerberos tickets.
These values should not be modified unless you are familiar with the
Kerberos system.
There are extensive notes in the LPRng source code concerning these values.
The kerberos_service
value supplies the name of the service
to be used when generating a ticket.
It is stronly recommended that the kerberos_id
entry
be used instead.
In order to use kerberos authentication,
the user will need to obtain a ticket from the Kerberos ticket server.
This is done using kinit
.
No other actions are required by the user.
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.
cd LPRng
./configure
make MIT_KERBEROS4=1
Options used:
auth=kerberos4
use Kerberos4 authenticationkerberos_id=
destination server key used by clientskerberos_server_principal=
alias for kerberos_idExample printcap entry:
pr:
:lp=pr@wayoff
:auth=kerberos4
:kerberos_id=lpr@wayoff.ASTART.COM
The configuration information for Kerberos4 and Kerberos5 is identical and differ only in the authentication type. Note that only client to server authentication is supported.
LPRng has built-in support for using MD5 digests as an authentication method. The implementation is provided as an example of how to add user level authentication into the LPRng system.
The method used to do authentication is very simple. Each user has a file containing a set of keys that are used to salt an md5 hash. The information being transferred has its md5 checksum calculated using this salt, and is then transferred to the destination, along with the md5 hash result. At the destination the server will get the user id, obtain the salt value from a key file, and then calculate the md5 hash value. If the two are in agreement, authentication is successful.
The keyfile used for md5 authentication contains an id followed by a text string whose binary value is used as a hash key:
id1=key
id2=key
Example:
lpr@h2=tadf79asd%^1asdf
lpr@h1=fdfa%$^&^%$
Options used:
auth=md5
use MD5 authenticationauth_forward=md5
forward using MD5 authenticationmd5_id=
id for servermd5_forward_id=
id for servermd5_server_keyfile=
server keyfileExample printcap entry:
pr:
:lp=pr@wayoff
:auth=md5
:md5_id=lpr@wayoff.com
pr:server
:auth_forward=md5
:md5_id=lpr@wayoff.com
:md5_server_keyfile
:md5_forward_id=lpr@faroff.com
The md5_id
value is used by the client to obtain
a hash key that is used to salt the md5 calculation for client to server
transfers.
The md5_forward_id
value is used by the server to obtain
a hash key that is used to salt the md5 calculation for server to server transfers.
The md5_server_keyfile
contains the keys of users;
the id sent as the connection information is used to obtain the key from the file.
To set up md5 authentication, all that is needed is the following.
user1@host1=asdfasdfadf
user2@host2=a8789087asddasdf
pr:
:lp=pr@wayoff
:auth=md5
:md5_id=lpr@wayoff.com
lpr@wayoff=user1@host1 asdfasdfadf
The first entry corresponds to the md5_id
value in the printcap.
The second field is the AUTHUSER
value supplied to the server
and which will be used to look up the key in the servers key file.
Finally,
the last field is the salt value for the md5 calculation.Options used:
MD5KEYFILE=5
location of user keyfileThe MD5KEYFILE
environment variable contains the path to the
user keytab file.
Additional types of authentication support can be added very easily to LPRng by using the following conventions and guidelines.
First, the authentication method can be connection based or transfer based. Connection based authentication involves the LPRng client or server opening a connection to the remote server, having the authentication protocol provide authentication information, and then having no further interaction with the system. This is the easiest to implement and understand method. Code needs to be provided to do a simple authentication exchange between the two ends of the connection, after which no other action needs to be taken.
Transfer based authentication is more complex, but allows encrypted transfers of information between the two systems. A connection is established between client and server (or server and server), and an initial protocol exchange is performed. Then the authentication module transfers the command or job information to the destination, where is it unpacked and/or decrypted. The internal LPD server facilities are then invoked by the authentication module, which also provides a destination for any error message or information destined for the client. The authentication module will encrpt or encode this information and then send it to the client program. This type of authentication is more complex, but provides a higher degree of security and reliability than the simple connection based system.
By convention,
printcap entries
auth=XXX
and
auth_forward=XXX
specifies that authentication protocol XXX
is to be used for client to server
and for server to server transfers respectively.
Similarly,
the server receiving an authentication request must have a
XXX_id=name
entry in the printcap or configuration information.
This allows several different authentication protocols to be accepted
by a server.
By convention,
printcap and configuration entries of the form
XXX_key
contain configuration information for the XXX
authentication protocol.
As part of the authentication support process the XXX_key
values
are extracted from the printcap and configuration files
and placed in a simple database for the authentication support module.
If you are using a routing filter,
then you can also place
XXX_key
information in the routing entry for each file,
and this will be used for sending the job to the specified destination.
The LPRng/src/common/sendauth.c
file has the following entries at the end.
#define SENDING
#include "user_auth.stub"
struct security SendSecuritySupported[] = {
/* name, config_tag, connect, send, receive */
{ "kerberos4", "kerberos", Send_krb4_auth, 0, 0 },
{ "kerberos*", "kerberos", 0, Krb5_send },
{ "pgp", "pgp", 0, Pgp_send },
#if defined(USER_SEND)
USER_SEND
#endif
{0}
};
This is an example of how to add user level authentication support.
The user_auth.stub
file contains the source code for the various modules authentication
modules.
You can replace this file with your own version
if desired.
The following fields are used.
The authentication name.
The auth=XXX
printcap or configuration value will cause the
name
fields to be searched using a
glob match.
When a match is found,
the config_tag
value is used to search
the printcap and configuration entries for information.
If the config_tag
field has value XXX
,
then entries with keys XXX_key
will be extracted for use
by the authentication code.
Routine to call to support connection
level authentication.
This routine is responsible for connection establishment and
protocol handshake.
If the value is 0,
then the send
field value will be used.
Routine to call to support transfer
level authentication.
The send
routine is provided a file and a connection to the remote server,
and is responsible for the transferring files.
The
LPRng/src/common/lpd_secure.c
file has the following information at the end:
#define RECEIVE 1
#include "user_auth.stub"
struct security ReceiveSecuritySupported[] = {
/* name, config_tag, connect, send, receive */
#if defined(HAVE_KRB_H) && defined(MIT_KERBEROS4)
{ "kerberos4", "kerberos", 0, 0, 0 },
#endif
#if defined(HAVE_KRB5_H)
{ "kerberos*", "kerberos", 0, 0, Krb5_receive },
#endif
{ "pgp", "pgp", 0, 0, Pgp_receive, },
#if defined(USER_RECEIVE)
/* this should have the form of the entries above */
USER_RECEIVE
#endif
{0}
};
This information matches the same information in the sendauth.c
file.
When the authentication request arrives at the server,
the name
field values are searched for a match,
and then the config_tag
value is used to get extract configuration
information from the database for the protocol.
The receive
routine is then called and is expected to handle the remaining
steps of the authentication protocol.
If the routine exits with a 0 value then the lpd server expects
connection
level authentication has been done and proceeds to
simply transfer information using the standard RFC1179 protocol steps.
A non-zero return value indicates an error and an error is reported
to the other end of the connection.
If the receive
module is to perform transfer
level authentication,
then the module carries out the necessary steps to transfer the command and/or
job information.
It then calls the necessary internal LPRng routine to implement the desired
services.
After finishing the requested work,
these routines return to the calling authentication module,
which then will transfer data, close the connection to the
remote system,
and return to the calling system.
The combination of 0 return value and closed connection
indicates successful transfer level authentication to the server.
The user_auth.stub
file contains the following code that sets the
USER_SEND
variable:
#if defined(SENDING)
extern int md5_send();
# define USER_SEND \
{ "md5", "md5", md5_send, 0, md5_receive },
#endif
If the SENDING
value has been defined,
this causes the prototype for md5_send()
to be place in the file
and the USER_SEND
value to be defined.
This will cause the md5
authentication information to be placed in the
correct table.
Rather than go into a detailed description of the code,
the user_auth.stub
file contains extremely detailed examples
as well as several working versions of authentication information.
It is recommended that the user start with one of these and then
modify it to suit themselves.