/* Copyright (c) 1997, 1998, 1999 Thorsten Kukuk
   Author: Thorsten Kukuk <kukuk@suse.de>

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software Foundation,
   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#define _GNU_SOURCE

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <memory.h>
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#else
#include "lib/compat/getopt.h"
#endif
#include <syslog.h>
#include <signal.h>
#include <locale.h>
#include <libintl.h>
#include <rpc/rpc.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <rpc/key_prot.h>
#include <rpc/pmap_clnt.h>
#ifdef HAVE_DOOR_H
#include <door.h>
#endif

#ifndef _
#define _(String) gettext (String)
#endif

#include "keyserv.h"
#include "log_msg.h"

#ifndef KEYSERVSOCK
#define KEYSERVSOCK "/var/run/keyservsock"
#endif
#ifdef HAVE_DOOR_H
#ifndef KEYSERVDOOR
#define KEYSERVDOOR "/var/run/keyservdoor"
#endif
#define MAGIC_COOKIE ((void*) 0xbeefaffe)
#endif

char ROOTKEY[] = "/etc/.rootkey";

extern void detachfromtty (void);
extern void passwd2des (char *pw, char *key);

des_block masterkey;

struct cmessage {
  struct cmsghdr cmsg;
  struct ucred cmcred;
};

static int
__rpc_get_local_uid (uid_t *uid, SVCXPRT *transp)
{
  struct cmessage *cm;

  if (transp->xp_verf.oa_length < sizeof (struct cmessage) ||
      transp->xp_verf.oa_base == NULL ||
      transp->xp_verf.oa_flavor != AUTH_UNIX)
    return -1;

  cm = (struct cmessage *)transp->xp_verf.oa_base;
  if (cm->cmsg.cmsg_type != SCM_CREDENTIALS)
    return -1;

  *uid = cm->cmcred.uid;

  return 0;
}

static int
root_auth (SVCXPRT *trans, struct svc_req *rqstp, uid_t *uid)
{
  struct sockaddr_in *remote;

  remote = svc_getcaller (trans);
  if (remote->sin_family == AF_INET)
    {
      /* User used the old /usr/etc/keyenvoy program */
      if (ntohs (remote->sin_port) >= IPPORT_RESERVED ||
	  ntohl (remote->sin_addr.s_addr) != INADDR_LOOPBACK)
	{
	  if (debug_flag)
	    log_msg (LOG_DEBUG, _("\tnot local privileged process"));
	  return 0;
	}
      if (rqstp->rq_cred.oa_flavor != AUTH_UNIX)
	{
	  if (debug_flag)
	    log_msg (LOG_DEBUG, _("\tnot unix authentication (is %d)"),
		     rqstp->rq_cred.oa_flavor);
	  return 0;
	}
      *uid = ((struct authunix_parms *) rqstp->rq_clntcred)->aup_uid;
      return 1;
    }

  if (__rpc_get_local_uid (uid, trans) < 0)
    {
      if (debug_flag)
	log_msg (LOG_DEBUG, _("__rpc_get_local_uid failed"));
      return 0;
    }

  if (debug_flag)
    log_msg (LOG_DEBUG, _("local uid: %ld"), *uid);
  if (*uid == 0)
    return 1;
  if (rqstp->rq_cred.oa_flavor == AUTH_UNIX)
    {
      if (((uid_t) ((struct authunix_parms *)
		    rqstp->rq_clntcred)->aup_uid) == *uid)
	return 1;
      else
	{
	  if (debug_flag)
	    log_msg (LOG_DEBUG, _("local uid %ld mismatches auth %ld"), *uid,
		     ((struct authunix_parms *) rqstp->rq_clntcred)->aup_uid);
	  return 0;
	}
    }
  else
    {
      if (debug_flag)
	log_msg (LOG_DEBUG, _("not unix authentication"));
      return 0;
    }
}


static void
key_prog_2 (struct svc_req *rqstp, SVCXPRT *transp)
{
  union
  {
    keybuf key_set_1_arg;
    cryptkeyarg key_encrypt_1_arg;
    cryptkeyarg key_decrypt_1_arg;
    netnamestr key_getcred_1_arg;
    cryptkeyarg2 key_encrypt_pk_2_arg;
    cryptkeyarg2 key_decrypt_pk_2_arg;
    key_netstarg key_net_put_2_arg;
    netobj key_get_conv_2_arg;
  }
  argument;
  union {
    keystatus key_set_2_res;
    cryptkeyres key_encrypt_2_res;
    cryptkeyres key_decrypt_2_res;
    des_block key_gen_2_res;
    getcredres key_getcred_2_res;
    cryptkeyres key_encrypt_pk_2_res;
    cryptkeyres key_decrypt_pk_2_res;
    keystatus key_net_put_2_res;
    key_netstres key_net_get_2_res;
    cryptkeyres key_get_conv_2_res;
  } result;
  bool_t retval;
  xdrproc_t xdr_argument, xdr_result;
  bool_t (*local) (char *, void *, uid_t);
  struct sockaddr_in remote;
  int check_auth;
  uid_t uid;

  check_auth = 0;
  remote = *svc_getcaller (transp);
  if (debug_flag)
    log_msg (LOG_DEBUG, _("key_prog_2 [Connect from: %s:%d, call proc %d]"),
	     inet_ntoa (remote.sin_addr), ntohs (remote.sin_port),
	     rqstp->rq_proc);

  switch (rqstp->rq_proc)
    {
    case NULLPROC:		/* 0 */
      (void) svc_sendreply (transp, (xdrproc_t) xdr_void, (char *) NULL);
      return;

    case KEY_SET:		/* 1 */
      xdr_argument = (xdrproc_t) xdr_keybuf;
      xdr_result = (xdrproc_t) xdr_keystatus;
      local = (bool_t (*)(char *, void *, uid_t)) key_set_svc;
      check_auth = 1;
      break;

    case KEY_ENCRYPT:		/* 2 */
      xdr_argument = (xdrproc_t) xdr_cryptkeyarg;
      xdr_result = (xdrproc_t) xdr_cryptkeyres;
      local = (bool_t (*)(char *, void *, uid_t)) key_encrypt_svc;
      check_auth = 1;
      break;

    case KEY_DECRYPT:		/* 3 */
      xdr_argument = (xdrproc_t) xdr_cryptkeyarg;
      xdr_result = (xdrproc_t) xdr_cryptkeyres;
      local = (bool_t (*)(char *, void *, uid_t)) key_decrypt_svc;
      check_auth = 1;
      break;

    case KEY_GEN:		/* 4 */
      xdr_argument = (xdrproc_t) xdr_void;
      xdr_result = (xdrproc_t) xdr_des_block;
      local = (bool_t (*)(char *, void *, uid_t)) key_gen_svc;
      check_auth = 0;
      break;

    case KEY_GETCRED:		/* 5 */
      xdr_argument = (xdrproc_t) xdr_netnamestr;
      xdr_result = (xdrproc_t) xdr_getcredres;
      local = (bool_t (*)(char *, void *, uid_t)) key_getcred_svc;
      check_auth = 0;
      break;

    case KEY_ENCRYPT_PK:	/* 6 */
      xdr_argument = (xdrproc_t) xdr_cryptkeyarg2;
      xdr_result = (xdrproc_t) xdr_cryptkeyres;
      local = (bool_t (*)(char *, void *, uid_t)) key_encrypt_pk_svc;
      check_auth = 1;
      break;

    case KEY_DECRYPT_PK:	/* 7 */
      xdr_argument = (xdrproc_t) xdr_cryptkeyarg2;
      xdr_result = (xdrproc_t) xdr_cryptkeyres;
      local = (bool_t (*)(char *, void *, uid_t)) key_decrypt_pk_svc;
      check_auth = 1;
      break;

    case KEY_NET_PUT:		/* 8 */
      xdr_argument = (xdrproc_t) xdr_key_netstarg;
      xdr_result = (xdrproc_t) xdr_keystatus;
      local = (bool_t (*)(char *, void *, uid_t)) key_net_put_svc;
      check_auth = 1;
      break;

    case KEY_NET_GET:		/* 9 */
      xdr_argument = (xdrproc_t) xdr_void;
      xdr_result = (xdrproc_t) xdr_key_netstres;
      local = (bool_t (*)(char *, void *, uid_t)) key_net_get_svc;
      check_auth = 1;
      break;

    case KEY_GET_CONV:		/* 10 */
      xdr_argument = (xdrproc_t) xdr_keybuf;
      xdr_result = (xdrproc_t) xdr_cryptkeyres;
      local = (bool_t (*)(char *, void *, uid_t)) key_get_conv_svc;
      check_auth = 1;
      break;

    default:
      svcerr_noproc (transp);
      return;
    }
  if (check_auth)
    if (root_auth (transp, rqstp, &uid) == 0)
      {
	svcerr_weakauth (transp);
	return;
      }

  memset ((char *) &argument, 0, sizeof (argument));
  if (!svc_getargs (transp, xdr_argument, (caddr_t) &argument))
    {
      if (debug_flag)
	log_msg (LOG_DEBUG, _("svc_getargs fails!"));
      svcerr_decode (transp);
      return;
    }
  retval = (*local) ((char *) &argument, (void *) &result, uid);
  if (retval > 0 && !svc_sendreply (transp, xdr_result, (char *) &result))
    {
      svcerr_systemerr (transp);
    }
  if (!svc_freeargs (transp, xdr_argument, (caddr_t) &argument))
    {
      fprintf (stderr, _("unable to free arguments"));
      exit (1);
    }
  return;
}

#ifdef HAVE_DOOR_H

static void
key_prog_2_door (void *cookie, char *argp, size_t arg_size,
		 door_desc_t *dp __attribute__ ((unused)),
		 size_t n_desc __attribute__ ((unused)))
{
  XDR xdrs;
  union
  {
    keybuf key_set_1_arg;
    cryptkeyarg key_encrypt_1_arg;
    cryptkeyarg key_decrypt_1_arg;
    netnamestr key_getcred_1_arg;
    cryptkeyarg2 key_encrypt_pk_2_arg;
    cryptkeyarg2 key_decrypt_pk_2_arg;
    key_netstarg key_net_put_2_arg;
    netobj key_get_conv_2_arg;
  }
  argument;
  union {
    keystatus key_set_2_res;
    cryptkeyres key_encrypt_2_res;
    cryptkeyres key_decrypt_2_res;
    des_block key_gen_2_res;
    getcredres key_getcred_2_res;
    cryptkeyres key_encrypt_pk_2_res;
    cryptkeyres key_decrypt_pk_2_res;
    keystatus key_net_put_2_res;
    key_netstres key_net_get_2_res;
    cryptkeyres key_get_conv_2_res;
  } result;
  bool_t retval;
  xdrproc_t xdr_argument = NULL;
  xdrproc_t xdr_result = NULL;
  bool_t (*local) (char *, void *, uid_t) = NULL;
  char *return_ptr = NULL;
  u_long return_len = 0;
  u_long xdr_len;
  door_cred_t cred;
  u_long rq_proc;
  uid_t uid;

  if (cookie != MAGIC_COOKIE)
    {
      printf ("ERROR: shouldn't have cookie != %lx",
	      (long) MAGIC_COOKIE);
      abort ();
    }

  if (door_cred (&cred) < 0)
    {
      log_msg (LOG_ERR, _("door_cred failed: %s"), strerror (errno));
      abort ();
    }

  if (arg_size < 1)
    {
      return_ptr = NULL;
      return_len = 0;
    }

  uid = cred.dc_euid;
  memcpy (&rq_proc, argp, sizeof (u_long));
  memcpy (&xdr_len, &argp[sizeof (u_long)], sizeof (u_long));
  printf (" RPC function number: %ld\n", rq_proc);
  printf (" XDR length: %ld\n", xdr_len);

  if (debug_flag)
    log_msg (LOG_DEBUG,
	     _("key_prog_2 [Connect from door interface, call proc %d]"),
	     rq_proc);

  switch (rq_proc)
    {
    case NULLPROC:		/* 0 */
      if (door_return (return_ptr, return_len, NULL, 0) < 0)
	{
	  printf("*** shouldn't get here ***\n");
	  abort();
	}
      return;

    case KEY_SET:		/* 1 */
      xdr_argument = (xdrproc_t) xdr_keybuf;
      xdr_result = (xdrproc_t) xdr_keystatus;
      local = (bool_t (*)(char *, void *, uid_t)) key_set_svc;
      break;

    case KEY_ENCRYPT:		/* 2 */
      xdr_argument = (xdrproc_t) xdr_cryptkeyarg;
      xdr_result = (xdrproc_t) xdr_cryptkeyres;
      local = (bool_t (*)(char *, void *, uid_t)) key_encrypt_svc;
      break;

    case KEY_DECRYPT:		/* 3 */
      xdr_argument = (xdrproc_t) xdr_cryptkeyarg;
      xdr_result = (xdrproc_t) xdr_cryptkeyres;
      local = (bool_t (*)(char *, void *, uid_t)) key_decrypt_svc;
      break;

    case KEY_GEN:		/* 4 */
      xdr_argument = (xdrproc_t) xdr_void;
      xdr_result = (xdrproc_t) xdr_des_block;
      local = (bool_t (*)(char *, void *, uid_t)) key_gen_svc;
      break;

    case KEY_GETCRED:		/* 5 */
      xdr_argument = (xdrproc_t) xdr_netnamestr;
      xdr_result = (xdrproc_t) xdr_getcredres;
      local = (bool_t (*)(char *, void *, uid_t)) key_getcred_svc;
      break;

    case KEY_ENCRYPT_PK:	/* 6 */
      xdr_argument = (xdrproc_t) xdr_cryptkeyarg2;
      xdr_result = (xdrproc_t) xdr_cryptkeyres;
      local = (bool_t (*)(char *, void *, uid_t)) key_encrypt_pk_svc;
      break;

    case KEY_DECRYPT_PK:	/* 7 */
      xdr_argument = (xdrproc_t) xdr_cryptkeyarg2;
      xdr_result = (xdrproc_t) xdr_cryptkeyres;
      local = (bool_t (*)(char *, void *, uid_t)) key_decrypt_pk_svc;
      break;

    case KEY_NET_PUT:		/* 8 */
      xdr_argument = (xdrproc_t) xdr_key_netstarg;
      xdr_result = (xdrproc_t) xdr_keystatus;
      local = (bool_t (*)(char *, void *, uid_t)) key_net_put_svc;
      break;

    case KEY_NET_GET:		/* 9 */
      xdr_argument = (xdrproc_t) xdr_void;
      xdr_result = (xdrproc_t) xdr_key_netstres;
      local = (bool_t (*)(char *, void *, uid_t)) key_net_get_svc;
      break;

    case KEY_GET_CONV:		/* 10 */
      xdr_argument = (xdrproc_t) xdr_keybuf;
      xdr_result = (xdrproc_t) xdr_cryptkeyres;
      local = (bool_t (*)(char *, void *, uid_t)) key_get_conv_svc;
      break;

    default:
      {
	u_long res = PROC_UNAVAIL;
	return_ptr = (char *)&res;
	door_return (return_ptr, sizeof (u_long), NULL, 0);
	abort ();
      }
      return;
    }

  memset (&argument, '\0', sizeof (argument));
  xdrmem_create (&xdrs, &argp[2 * sizeof (u_long)], xdr_len, XDR_DECODE);
  if (!xdr_argument (&xdrs, &argument))
    {
      u_long res = SYSTEM_ERR;
      return_ptr = (char *)&res;
      xdr_destroy (&xdrs);
      door_return (return_ptr, sizeof (u_long), NULL, 0);
      abort ();
    }
  xdr_destroy (&xdrs);

  retval = (*local) ((char *) &argument, (void *) &result, uid);
  if (retval == FALSE)
    {
      u_long res = SYSTEM_ERR;
      return_ptr = (char *)&res;
      door_return (return_ptr, sizeof (u_long), NULL, 0);
      abort ();
    }

  return_len = xdr_sizeof (xdr_result, &result);
  if ((return_ptr = alloca (return_len + 2 * sizeof (u_long))) == NULL)
    {
      u_long res = SYSTEM_ERR;
      return_ptr = (char *)&res;
      door_return (return_ptr, sizeof (u_long), NULL, 0);
      abort ();
    }
  memset (return_ptr, '\0', return_len + 2 * sizeof (u_long));

  xdrmem_create(&xdrs, &return_ptr[2 * sizeof (u_long)], return_len,
		XDR_ENCODE);
  if (!xdr_result (&xdrs, &result))
    {
      u_long res = SYSTEM_ERR;
      return_ptr = (char *)&res;
      xdr_destroy (&xdrs);
      door_return (return_ptr, sizeof (u_long), NULL, 0);
      abort ();
    }
  xdr_destroy (&xdrs);

  memcpy (&return_ptr[sizeof (u_long)], &return_len, sizeof (u_long));

  printf("  replying: %ld bytes\n", return_len + 2 * sizeof (u_long));

  if (door_return (return_ptr, return_len + 2 * sizeof (u_long), NULL, 0) < 0)
    {
      printf("*** shouldn't get here ***\n");
      abort();
    }
}
#endif

/* In the event that we don't get a root password, we try to randomize the
   master key the best we can. */
static void
random_master (des_block * master)
{
  int i;
  int seed;
  struct timeval tv;
  int shift;
  int on = 1;
  int fd = open ("/dev/random", O_RDONLY);

  if (fd >= 0 && ioctl (fd, FIONBIO, &on) == 0 &&
      read (fd, &master->key, sizeof (master->key)) == sizeof (master->key))
    {
      close (fd);
      return;
    }
  if (fd >= 0)
    close (fd);

  seed = 0;
  for (i = 0; i < 1024; i++)
    {
      gettimeofday (&tv, (struct timezone *) NULL);
      shift = i % 8 * sizeof (int);
      seed ^= (tv.tv_usec << shift) | (tv.tv_usec >> (32 - shift));
    }
  srandom (seed);
  master->key.low = random ();
  master->key.high = random ();
  srandom (seed);
}

/* Read the root key from /etc/.rootkey or prompt for it. */
static int
getrootkey (des_block * master, int prompt)
{
  key_netstarg netstore;
  char *passwd;
  char name[MAXNETNAMELEN + 1];
  char secret[HEXKEYBYTES + 1];
  int fd;

  if (!prompt)
    {
      /* Read secret key out of $ROOTKEY */
      fd = open (ROOTKEY, O_RDONLY, 0);
      if (fd < 0)
	{
	  random_master (master);
	  return 0;
	}
      if (read (fd, secret, HEXKEYBYTES) < 0)
	{
	  fprintf (stderr, _("Invalid %s\n"), ROOTKEY);
	  close (fd);
	  return 0;
	}
      close (fd);
      if (!getnetname (name))
	{
	  fprintf (stderr,
		   _("keyserv: failed to generate host's netname when establishing root's key.\n"));
	  return 0;
	}
      memcpy (netstore.st_priv_key, secret, HEXKEYBYTES);
      memset (netstore.st_pub_key, 0, HEXKEYBYTES);
      netstore.st_netname = name;
      if (pk_netput (0, &netstore) != KEY_SUCCESS)
	{
	  fprintf (stderr,
		   _ ("keyserv: could not set root's key and netname.\n"));
	  return 0;
	}
    }
  else
    {
      /* Decrypt entry to get secret key */
      passwd = getpass (_ ("root password:"));
      passwd2des (passwd, (char *) master);
      getnetname (name);
      if (!getsecretkey (name, secret, passwd))
	{
	  fprintf (stderr, _("Can't find %s's secret key\n"), name);
	  return 0;
	}
      if (secret[0] == 0)
	{
	  fprintf (stderr, _("Invalid password for %s\n"), name);
	  return 0;
	}
      pk_setkey (0, secret);
      /* Store it for future use in $ROOTKEY, if possible. */
      fd = open (ROOTKEY, O_WRONLY | O_TRUNC | O_CREAT, 0);
      if (fd > 0)
	{
	  char newline = '\n';
	  write (fd, secret, strlen (secret));
	  write (fd, &newline, sizeof (newline));
	  close (fd);
	}
    }
  return 1;
}

/* Clean up if we quit the program. */
static void
sig_quit (int sig __attribute__ ((unused)))
{
#ifdef HAVE_DOOR_H
  fdetach (KEYSERVDOOR);
  unlink (KEYSERVDOOR);
#endif
  pmap_unset (KEY_PROG, KEY_VERS);
  pmap_unset (KEY_PROG, KEY_VERS2);
  unlink (KEYSERVSOCK);

  exit (0);
}

/* Reload something ? */
static void
sig_hup (int sig __attribute__ ((unused)))
{
  /* Nothing do do for us in the moment */
}

/* Name and version of program.  */
/* Print the version information.  */
static inline void
print_version (void)
{
  fprintf (stdout, "keyserv (%s) %s\n", PACKAGE, VERSION);
  /*  fprintf (stdout, gettext ("\
Copyright (C) %s Thorsten Kukuk.\n\
This is free software; see the source for copying conditions.  There is NO\n\
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
"), "1998");
  fprintf (stdout, _("Written by %s.\n"), "Thorsten Kukuk"); */
}

static inline void
print_usage (void)
{
  fputs (_("Usage: keyserv [-d] [-D] [-n]\n"), stdout);
}

static void
print_help (void)
{
  print_usage ();
  fputs (_("keyserv - server for storing private encryption keys\n\n"),
         stdout);

  fputs (_("  -d             Disable use of default keys for \"nobody\"\n"),
         stdout);
  fputs (_("  -D, --debug    Run in debug mode, don't fork\n"), stdout);
  fputs (_("  -n             prompt for root's secret key\n"),
         stdout);
  fputs (_("  --help         Give this help list\n"), stdout);
  fputs (_("  --usage        Give a short usage message\n"), stdout);
  fputs (_("  --version      Print program version\n"), stdout);
}

static inline void
print_error (void)
{
  const char *program = "keyserv";

  fprintf (stderr,
           _("Try `%s --help' or `%s --usage' for more information.\n"),
           program, program);
}

/*
 * Hack to allow the keyserver to use AUTH_DES (for authenticated
 * NIS+ calls, for example).  The only functions that get called
 * are key_encryptsession_pk, key_decryptsession_pk, and key_gendes.
 *
 * The approach is to have the keyserver fill in pointers to local
 * implementations of these functions, and to call those in key_call().
 */

extern cryptkeyres *(*__key_encryptsession_pk_LOCAL) (char *, struct svc_req *);
extern cryptkeyres *(*__key_decryptsession_pk_LOCAL) (char *, struct svc_req *);
extern des_block *(*__key_gendes_LOCAL) (char *, struct svc_req *);

int
main (int argc, char **argv)
{
  SVCXPRT *transp;
  int nflag = 0;
  int sock = RPC_ANYSOCK;
#ifdef HAVE_DOOR_H
  int door;
#endif

  __key_encryptsession_pk_LOCAL =
    (cryptkeyres * (*)(char *, struct svc_req *)) &key_encrypt_pk_svc;
  __key_decryptsession_pk_LOCAL =
    (cryptkeyres * (*)(char *, struct svc_req *)) &key_decrypt_pk_svc;
  __key_gendes_LOCAL = (des_block * (*)(char *, struct svc_req *)) &key_gen_svc;

  setlocale (LC_MESSAGES, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  while (1)
    {
      int c;
      int option_index = 0;
      static struct option long_options[] =
      {
	{"version", no_argument, NULL, '\255'},
	{"usage", no_argument, NULL, '\254'},
	{"help", no_argument, NULL, '\253'},
	{"debug", no_argument, NULL, 'D'},
	{NULL, 0, NULL, '\0'}
      };

      c = getopt_long (argc, argv, "Dnd", long_options, &option_index);
      if (c == EOF)
	break;
      switch (c)
	{
	case 'n':
	  nflag++;
	  break;
	case 'd':
	  pk_nodefaultkeys ();
	  break;
	case 'D':
	  debug_flag = 1;
	  break;
	case '\255':
	  print_version ();
	  return 0;
	case '\253':
	  print_help ();
	  return 0;
	case '\254':
	  print_usage ();
	  return 0;
	default:
	  print_error ();
	  return 1;
	}
    }
  argc -= optind;
  argv += optind;

  if (argc != 0)
    {
      fputs (_("keyserv: To many parameters\n"), stderr);
      print_error ();
      return 1;
    }

  if (debug_flag)
    log_msg (LOG_DEBUG, "[Welcome to the Keyserver, version %s]", VERSION);

  if (geteuid () != 0)
    {
      fputs (_("keyserv must be run as root\n"), stderr);
      return 1;
    }

  if (!debug_flag)
    {
      int i;

      if (fork ())
        exit (0);

      for (i = 0; i < getdtablesize (); ++i)
	close (i);

      if (fork ())
        exit (0);

      chdir ("/");
    }

  openlog ("keyserv", LOG_PID, LOG_DAEMON);

  /* Ignore SIGPIPEs. */
  signal (SIGPIPE, SIG_IGN);
  signal (SIGTERM, sig_quit);
  signal (SIGINT, sig_quit);
  signal (SIGHUP, sig_hup);

  pmap_unset (KEY_PROG, KEY_VERS);
  pmap_unset (KEY_PROG, KEY_VERS2);
  unlink (KEYSERVSOCK);

  /* Initialize */
  umask (066);			/* paranoia */
  setmodulus (HEXMODULUS);
  getrootkey (&masterkey, nflag);

  transp = svcudp_create (RPC_ANYSOCK);
  if (transp == NULL)
    {
      fputs (_("cannot create udp service.\n"), stderr);
      return 1;
    }

  if (!svc_register (transp, KEY_PROG, KEY_VERS, key_prog_2, IPPROTO_UDP))
    {
      fputs (_("unable to register (KEY_PROG, KEY_VERS, udp).\n"), stderr);
      return 1;
    }
  if (!svc_register (transp, KEY_PROG, KEY_VERS2, key_prog_2, IPPROTO_UDP))
    {
      fputs (_("unable to register (KEY_PROG, KEY_VERS2, udp).\n"), stderr);
      return 1;
    }

  transp = svcunix_create (sock, 0, 0, KEYSERVSOCK);
  chmod (KEYSERVSOCK, 0666);
  if (transp == NULL)
    log_msg (LOG_INFO, _("cannot create AF_UNIX service."));
  else if (!svc_register (transp, KEY_PROG, KEY_VERS, key_prog_2, 0))
    log_msg (LOG_INFO, _("unable to register (KEY_PROG, KEY_VERS, unix)"));
  else if (!svc_register (transp, KEY_PROG, KEY_VERS2, key_prog_2, 0))
    log_msg (LOG_INFO, _("unable to register (KEY_PROG, KEY_VERS2, unix)"));

#ifdef HAVE_DOOR_H
  if ((door = door_create (key_prog_2_door, (void*) MAGIC_COOKIE, 0)) < 0)
    log_msg (LOG_INFO, _("unable to create door: %s"), strerror (errno));
  else if (creat (KEYSERVDOOR, 0666) < 0)
    log_msg (LOG_INFO, _("creat (%s, 0666) failed: %s"), KEYSERVDOOR,
	     strerror (errno));
  else if (fattach (door, KEYSERVDOOR) < 0)
    {
      log_msg (LOG_INFO, _("fattach failed: %s"), strerror (errno));
      unlink (KEYSERVDOOR);
    }
  else if (chmod (KEYSERVDOOR, 0666) < 0)
    {
      log_msg (LOG_INFO, _("chmod failed: %s"), strerror (errno));
      unlink (KEYSERVDOOR);
    }
#endif

  svc_run ();
  log_msg (LOG_ERR, _("svc_run returned"));
  return 1;
  /* NOTREACHED */
}
