Secure RPC weirdness
--------------------

During the initial design stages it looked as if the only deadlock problem
inherent in NIS+ was in the server (the svc_auth_des module needs to call
getpublickey() to lookup public keys, but if rpc.nisd calls getpublickey(),
it would end up trying to call itself). However, there is also a deadlock
condition in the client side which should be noted.

Consider what happens when you try to use Secure RPC and you are using
rpc.nisd with level 2 (DES) security. The nislib client library needs
to call authdes_create() to set up its credentials. However, this involves
making a call to getpublickey() to learn the public key of the server.
Making a getpublickey() call in turn involves making a call to rpc.nisd,
which in turn requires creating DES credentials. This requires a call to
getpublickey(), which requires an NIS+ lookup, which requires a call to
getpublickey(), which requires an NIS+ lookup, which requires a call to
getpublickey(), and so on and so forth.

To break this cycle of recursion, Sun added a new call to the auth_des
module in TI-RPC called authdes_pk_seccreate() (which is called
authdes_pk_create() in FreeBSD). This function serves the same purpose
as authdes_create(), except that it allows the caller to specify  the
public key of the remote host directly. In the NIS+ context, the nislib
code will already know the public key of the NIS+ server since it is
supplied with a directory object as a result of a finddirectory search.
The authdes_pk_create() routine in turn uses key_encryptsession_pk(),
which is part of the keyserv version 2 RPC protocol.

Binding
-------

Binding is a lot more complicated than it is in NIS v2. With YP,
the following happens:

- A process calls one of the yplib functions (yp_match(), yp_first(),
  yp_next(), etc...). One of the things the client supplies is a
  domain name.
- The yplib routine calls _yp_dobind() to establish a binding to the
  specified domain.
- _yp_dobind() checks to see if a /var/yp/binding/<domain>.<version>
  file exists; if it does, it reads the info from this file and uses
  it to create an RPC CLIENT handle. The binding file contains the
  IP address and port of a server. If the binding file doesn't exist,
  _yp_dobind() tries to contact ypbind on the local host and asks it
  for binding information for the domain.
- If ypbind doesn't know anything, it returns failure and sends out a
  broadcast to the YPPROC_DOMAIN_NONACK procedure with the specified
  domain as an argument.
- Any server that serves the domain responds to ypbind, which then
  updates the binding file.
- Meanwhile, _yp_dobind() loops around retrying until ypbind suceeds
  or it exceeds the retry count and returns failure.

The important thing here is that ypbind only uses the address of the
first server that responds: this server's address is written to the
binding file while all others are ignored. The yplib code then only
has one address to deal with. If this server crashes or becomes
unreachable, the yplib code will once again poke ypbind, which will
send out another broadcast that will reach a slave server.

With NIS+, a client is initialized using the /var/nis/NIS_COLD_START
file. This file is an XDR'ed directory_obj structure which the client
can trust; it is set up by the system administrator using the nisinit
command. From this file, the client can obtain a list of NIS+ servers
that it can use to bootstrap its binding process. This includes the 
master server (which is always the first nis_server struct in the list) 
and all replicas. It also includes all their public keys, in the event 
that level 2 (DES) security is in use. Usually, the cold start file
contains a directory object for the client's default domain, however it
is not required for this to be so: as long as the directory object
describes a directory that is part of the overall server heirarchy to
which the client belongs, the client can find its way to the server
that it really needs.

It is possible to rely on the cold start file exlusively, but this
can result in poor performance if it becomes necessary to query a
non-local domain: the nislib code has to query its local server to
learn the locations of the non-local ones. Consequently, a cache is
used that is managed by a server called nis_cachemr.

Therefore, NIS+ binding works like this:

- A process calles an nislib function with a name to be looked up.
  (I.e. nis_lookup("org_dir.foo.bar.", flags))
- The nislib routine invokes __nis_bind() in order to establish an
  RPC CLIENT handle to a server that serves the "foo.bar." domain.
- For efficiency, __nis_bind() maintains a small list of open RPC
  CLIENT handles to recently used servers. If __nis_bind() finds that
  it already has a handle for the desired domain, it uses that and
  bypasses the rest of the search (unless the time to live of the
  directory object has run out).
- __nis_bind() checks the cache to see if it has a directory_obj
  structure for the "foo.bar." directory. If it finds an exact match,
  it then scans its list of servers. If the caller specified the
  MASTER_ONLY, flag, then it immediately chooses the first one.
- If the caller didn't specify MASTER_ONLY and there's more than one
  server, __nis_bind() has to choose one from the list. For this, it
  broadcasts to the NULL_PROC of all the servers and chooses the server
  that responds the fastest. (The choosing algorithm is biased in
  favor of servers on the local network.)
- If there is an entry in the cache which is related to the desired
  domain (i.e. the "bar." domain), __nis_bind() queries the master
  server using the NIS_FINDDIRECTORY procedure to see if it knows
  about the child domain "foo.bar." If so, __nis_bind() adds the
  new directory_obj to the cache and continues as before.
- If there is no match in the cache, __nis_bind() calls the cold start
  server to see if _it_ knows about the desired domain.
- If there is no cache (because nis_cachemgr is not running), __nis_bind()
  has to call the cold start server for the information. If it doesn't have
  any useful information, the lookup has failed.

