/* Copyright (c) 1996, 1997, 1998 Thorsten Kukuk
   Author: Thorsten Kukuk <kukuk@vt.uni-paderborn.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.  */

#if defined(HAVE_CONFIG_H)
#include "config.h"
#endif

#include <rpc/des_crypt.h>
#include <rpc/key_prot.h>
#include <sys/param.h>
#include <locale.h>
#include <libintl.h>

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

#define _(String) gettext (String)

extern keystatus pk_get_conv_key (uid_t, keybuf, cryptkeyres *);

static const char *
strstatus (keystatus status)
{
  switch (status)
    {
    case KEY_SUCCESS:
      return "KEY_SUCCESS";
    case KEY_NOSECRET:
      return "KEY_NOSECRET";
    case KEY_UNKNOWN:
      return "KEY_UNKNOWN";
    case KEY_SYSTEMERR:
      return "KEY_SYSTEMERR";
    default:
      return _("(bad result code)");
    }
}

/* This is my secret key. Store it for me. */
bool_t
key_set_svc (opaque *key, keystatus *result, uid_t uid)
{
  if (debug_flag)
    {
      log_msg (LOG_DEBUG, "key_set_svc()");
      log_msg (LOG_DEBUG, "\tuid=%d, key=%.*s", uid, HEXKEYBYTES, key);
    }

  *result = pk_setkey (uid, key);
  if (debug_flag)
    log_msg (LOG_DEBUG, "\tresult=%s", strstatus (*result));

  return TRUE;
}

/* I want to talk to X. Encrypt a conversation key for me. */
bool_t
key_encrypt_svc (cryptkeyarg *argp, cryptkeyres *result, uid_t uid)
{
  if (debug_flag)
    {
      log_msg (LOG_DEBUG, "key_encrypt_svc()");
      log_msg (LOG_DEBUG, "\tuid=%d", uid);
      log_msg (LOG_DEBUG, "\tremotename=%s", argp->remotename);
      log_msg (LOG_DEBUG, "\tDES key=%08x%08x",
	       argp->deskey.key.high,
	       argp->deskey.key.low);
    }

  result->cryptkeyres_u.deskey = argp->deskey;
  result->status = pk_encrypt (uid, argp->remotename, NULL,
			       &result->cryptkeyres_u.deskey);

  if (debug_flag)
    {
      if (result->status == KEY_SUCCESS)
	log_msg (LOG_DEBUG, "\tEncrypt key=%08x%08x",
		 result->cryptkeyres_u.deskey.key.high,
		 result->cryptkeyres_u.deskey.key.low);
      else
	log_msg (LOG_DEBUG, "\tresult=%s", strstatus (result->status));
    }

  return TRUE;
}

/* X just sent me a message. Decrypt the conversation key for me. */
bool_t
key_decrypt_svc (cryptkeyarg *argp, cryptkeyres *result, uid_t uid)
{
  if (debug_flag)
    {
      log_msg (LOG_DEBUG, "key_decrypt_svc()");
      log_msg (LOG_DEBUG, "\tuid=%d", uid);
      log_msg (LOG_DEBUG, "\tremotename=%s", argp->remotename);
      log_msg (LOG_DEBUG, "\tDES key=%08x%08x",
	       argp->deskey.key.high,
	       argp->deskey.key.low);
    }

  result->cryptkeyres_u.deskey = argp->deskey;
  result->status = pk_decrypt (uid, argp->remotename, NULL,
			       &result->cryptkeyres_u.deskey);

  if (debug_flag)
    {
      if (result->status == KEY_SUCCESS)
	log_msg (LOG_DEBUG, "\tDecrypt key=%08x%08x",
		 result->cryptkeyres_u.deskey.key.high,
		 result->cryptkeyres_u.deskey.key.low);
      else
	log_msg (LOG_DEBUG, "\tresult=%s", strstatus (result->status));
    }

  return TRUE;
}

/* Generate a secure conversation key for me. */
bool_t
key_gen_svc (void *argp __attribute__ ((unused)), des_block *result,
	     uid_t uid __attribute__ ((unused)))
{
  struct timeval now;

  if (debug_flag)
    log_msg (LOG_DEBUG, "key_gen_svc()");

  gettimeofday (&now, (struct timezone *) NULL);
  result->key.high += (now.tv_sec ^ now.tv_usec);
  result->key.low += (now.tv_sec ^ now.tv_usec);
  ecb_crypt ((char *) &masterkey, (char *) result, sizeof (des_block),
	     DES_ENCRYPT | DES_HW);
  des_setparity ((char *) result);

  if (debug_flag)
    log_msg (LOG_DEBUG, "\tkey=%08x%08x",
	     result->key.high,
	     result->key.low);

  return TRUE;
}

/* Get me the uid, gid and group-access-list associated with this netname
   (for kernel which cannot use NIS). */
/* XXX THIS IS NOT THREAD SAFE !! */
bool_t
key_getcred_svc (netnamestr *name, getcredres *result,
		 uid_t uid __attribute__ ((unused)))
{
  static int gids[NGROUPS];
  struct unixcred *cred;

  if (debug_flag)
    {
      log_msg (LOG_DEBUG, "key_getcred_svc()");
      log_msg (LOG_DEBUG, "\tname=%s", *name);
    }

  cred = &result->getcredres_u.cred;
  cred->gids.gids_val = gids;
  if (!netname2user (*name, &cred->uid, &cred->gid,
		     &cred->gids.gids_len, gids))
    result->status = KEY_UNKNOWN;
  else
    result->status = KEY_SUCCESS;

  if (debug_flag)
    {
      if (result->status == KEY_SUCCESS)
	{
	  unsigned i;
	  log_msg (LOG_DEBUG, "\tuid=%d\n\tgid=%d\n\tgids=",
		   cred->uid, cred->gid);
	  for (i = 0; i < cred->gids.gids_len; i++)
	    {
	      log_msg (LOG_DEBUG, "%d", cred->gids.gids_val[i]);
	      if (i + 1 != cred->gids.gids_len)
		log_msg (LOG_DEBUG, ",");
	    }
	  log_msg (LOG_DEBUG, "");
	}
      else
	log_msg (LOG_DEBUG, "%s", strstatus (result->status));
    }

  return TRUE;
}

/* I want to talk to X. and I know X's public key.
   Encrypt a conversation key for me. */
bool_t
key_encrypt_pk_svc (cryptkeyarg2 *argp, cryptkeyres *result, uid_t uid)
{
  if (debug_flag)
    {
      log_msg (LOG_DEBUG, "key_encrypt_pk_svc()");
      log_msg (LOG_DEBUG, "\tuid=%d", uid);
      log_msg (LOG_DEBUG, "\tremotename=%s", argp->remotename);
      log_msg (LOG_DEBUG, "\tremotekey=%.*s", argp->remotekey.n_len,
	       argp->remotekey.n_bytes);
      log_msg (LOG_DEBUG, "\tDES key=%08x%08x",
	       argp->deskey.key.high,
	       argp->deskey.key.low);
    }

  result->cryptkeyres_u.deskey = argp->deskey;
  result->status = pk_encrypt (uid, argp->remotename, &(argp->remotekey),
			       &result->cryptkeyres_u.deskey);
  if (debug_flag)
    {
      if (result->status == KEY_SUCCESS)
	log_msg (LOG_DEBUG, "\tEncrypt key=%08x%08x",
		 result->cryptkeyres_u.deskey.key.high,
		 result->cryptkeyres_u.deskey.key.low);
      else
	log_msg (LOG_DEBUG, "\tresult=%s", strstatus (result->status));
    }

  return TRUE;
}

/* X just sent me a message. and I know X's public key
   Decrypt the conversation key for me. */
bool_t
key_decrypt_pk_svc (cryptkeyarg2 *argp, cryptkeyres *result, uid_t uid)
{
  if (debug_flag)
    {
      log_msg (LOG_DEBUG, "key_decrypt_pk_svc()");
      log_msg (LOG_DEBUG, "\tuid=%d", uid);
      log_msg (LOG_DEBUG, "\tremotename=%s", argp->remotename);
      log_msg (LOG_DEBUG, "\tremotekey=%.*s", argp->remotekey.n_len,
	       argp->remotekey.n_bytes);
      log_msg (LOG_DEBUG, "\tDES key=%08x%08x",
	       argp->deskey.key.high,
	       argp->deskey.key.low);
    }

  result->cryptkeyres_u.deskey = argp->deskey;
  result->status = pk_decrypt (uid, argp->remotename, &(argp->remotekey),
			       &result->cryptkeyres_u.deskey);

  if (debug_flag)
    {
      if (result->status == KEY_SUCCESS)
	log_msg (LOG_DEBUG, "\tDecrypt key=%08x%08x",
		 result->cryptkeyres_u.deskey.key.high,
		 result->cryptkeyres_u.deskey.key.low);
      else
	log_msg (LOG_DEBUG, "\tresult=%s", strstatus (result->status));
    }

  return TRUE;
}

/* Store my public key, netname and private key. */
bool_t
key_net_put_svc (key_netstarg *argp, keystatus *result, uid_t uid)
{
  if (debug_flag)
    {
      log_msg (LOG_DEBUG, "key_net_put_svc()");
      log_msg (LOG_DEBUG, "\tuid=%d", uid);
    }

  *result = pk_netput (uid, argp);

  return TRUE;
}

/* Retrieve my public key, netname and private key. */
bool_t
key_net_get_svc (void *argp __attribute__ ((unused)),
		 key_netstres *result, uid_t uid)
{
  if (debug_flag)
    {
      log_msg (LOG_DEBUG, "key_net_get_svc()");
      log_msg (LOG_DEBUG, "\tuid=%d", uid);
    }

  result->status = pk_netget (uid, &result->key_netstres_u.knet);

  if (debug_flag)
    log_msg (LOG_DEBUG, "\tresult=%s", strstatus (result->status));

  return TRUE;
}

/* Return me the conversation key that is constructed
   from my secret key and this publickey. */
bool_t
key_get_conv_svc (opaque *argp, cryptkeyres *result, uid_t uid)
{
  if (debug_flag)
    {
      log_msg (LOG_DEBUG, "key_get_conv_svc()");
      log_msg (LOG_DEBUG, "\tuid=%d", uid);
    }

  result->status = pk_get_conv_key (uid, argp, result);

  if (debug_flag)
    log_msg (LOG_DEBUG, "\tresult=%s", strstatus (result->status));

  return TRUE;
}
