Next Previous Contents

16. Authentication and Encryption

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.

16.1 Authentication

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:

  1. The received command must be checked for consistency, and the remote user and host must be determined.
  2. The remote user and host must be authenticated.
  3. The command and/or spooling operation must be carried out.
  4. The results must be returned to the remote system.

16.2 Identifiers

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.

16.3 RFC1179 Protocol Extensions

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.

16.4 Authentication Operations

Options used:

A 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.

  1. The 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.
  2. The 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.
  3. The authenticated transfer request sent to a server has one of the following forms, depending on the orginator:
    \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.

  4. The 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.
  5. When the authenticated transfer request is received, the destination will either return a single zero byte, or a non-zero byte value followed by additional refusal information. A refusal terminates the protocol exchange.
  6. Further exchanges are then determined by the authentication protocol specific requirements.
  7. Once the initial exchanges have been completed a user file and/or command will be transferred to the destination server.
  8. An authentication protcol specific AUTHFROM and AUTHUSER strings will be supplied to the lpd server for purposes of permission checking.
  9. The lpd server then carries out the requested operation, and will write error and status information into a file.
  10. After the requested activity has finished, protocol specific module transfer the status information in the file to the requesting system and terminate the protocol exchange.

16.5 Permission Checking

When an authenticated transfer has been performed, the following permission information will be provided.

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

16.6 PGP Authentication Support

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.

Printcap Configuration

Options used:

Example 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.

User Files and Environment Variables

Options used:

One problem with using PGP is the need to have users input their passphrases. The following methods can be used.

16.7 Using Kerberos 5 for Authentication

LPRng Kerberos 5 authentication is based on the Kerberos5-1.0.5 release as of March 28, 1999. This was obtained from MIT:

  1. ftp to ATHENA-DIST.MIT.EDU (18.159.0.42), login anonymous, password your_email_address
  2. Change into the directory '/pub/kerberos/
  3. Get the README files and look at the details of using FTP to get the distribution. Note that there are also patches available which you might want to use.

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.

Kerberos Installation Procedure

  1. Get the Kerberos 5 distribution.
  2. Compile and install the distribution.
  3. Create the /etc/krb5.conf, /usr/local/var/krb5kdc/kdc.conf files using templates from the src/conf-files subdirectory. See the Installation notes and the System Administrators Guide.
  4. Don't forget to create the /usr/local/var/krb5kdc/kdc.acl file; I did and it took me HOURS to figure out what was wrong...
  5. Start up the KDC and KADMIN servers - you might want to put the following in your rc.local or equivalent file:
    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
    
  6. use kadmin (or kadmin.local) to create principals for your users.
  7. Now you need to create principals for the lprng servers. I have been using lpr/hostname.REALM as a template- i.e. lpr/astart1.astart.com@ASTART.COM for an example.
  8. Do this for all the servers. You should use fully qualified domain names for the principals.
  9. Now you need to extract the keytab for each of the servers:
    kadmin ...
    ktadd -k file_for_host  lpr/hostname.REALM
    
  10. The 'file_for_host' contains the keytab information, which is the equivalent information for the server.
  11. Copy the 'file_for_host' to the server (you might want to encrypt or use a secure transfer for this). You need to put this in /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
    

Printcap Entries

Options used:

Example 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.

User Environment Variables and Files

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.

16.8 Using Kerberos 4 for Authentication

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

Printcap Entries

Options used:

Example 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.

16.9 Using MD5 for Authentication

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%$^&^%$

Printcap Entries

Options used:

Example 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.

User Environment Variables and Files

Options used:

The MD5KEYFILE environment variable contains the path to the user keytab file.

16.10 Adding Authentication Support

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.

Printcap Support

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.

Code Support

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.
name

The authentication name. The auth=XXX printcap or configuration value will cause the name fields to be searched using a glob match.

config_tag

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.

connect

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.

send

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.

Connection and Transfer Authentication

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.


Next Previous Contents