/* Copyright (C) 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 <string.h>
#include <rpc/rpc.h>
#include <rpcsvc/nis.h>
#include "nisd.h"
#include "log_msg.h"

static char *nis_netid_to_princ (char *netid, char *domain, enum_t flavor);

/* Access check routine. */
bool_t
nis_check_access (uint32_t action, uint32_t perms, const_nis_name princ,
		  const_nis_name owner, const_nis_name group)
{
  /* If securelevel == 0, all bets are off. */
  if (securelevel == 0)
    return TRUE;

  /* If 'nobody' allows the action, then return success. */
  if (NOBODY (perms, action))
    return TRUE;

  /* If 'world' allows the access and the principal has
     been authenticated (i.e. the principal name is not
     'nobody'), then return success. */
  if (WORLD (perms, action) && strcmp (princ, "nobody") != 0)
    return TRUE;

  /* If 'owner' allows access and the principal name is the
     same as the owner name, return success. */
  if (OWNER (perms, action) && strcmp (princ, owner) == 0)
    return TRUE;

  /* Lastly, if 'group' allows access and the principal
     is a member of the group, return success. */
  if (GROUP (perms, action) && nis_ismember (princ, group) == TRUE)
    return TRUE;

  /* Nothing worked: you're out of luck. */
  return FALSE;
}

/* Construct a principal name given RPC auth credentials. */
nis_name
get_nis_principal (struct svc_req *rqstp)
{
  nis_name principal = NULL;

  switch (rqstp->rq_cred.oa_flavor)
    {
    case AUTH_DES:
      {
	char *ptr, *domain;
	struct authdes_cred *creds =
	  (struct authdes_cred *) rqstp->rq_clntcred;

	log_msg (LOG_DEBUG, "AUTH_DES");

	ptr = strchr (creds->adc_fullname.name, '@');
	if (ptr == NULL)
	  {
	    log_msg (LOG_ERR, "bogus netname: %s", creds->adc_fullname.name);
	    principal = strdup ("nobody");
	  }
	++ptr;
	domain = alloca (strlen (ptr) + 2); /* "." + "\0" */
	ptr = stpcpy (domain, ptr);
	if (ptr[-1] != '.')
	  {
	    *ptr++ = '.';
	    *ptr = '\0';
	  }

	principal = nis_netid_to_princ (creds->adc_fullname.name, domain,
					AUTH_DES);
	if (principal == NULL)
	  {
	    if (verbose)
	      log_msg (LOG_DEBUG, "warning: DES cred lookup failed");
	    principal = strdup ("nobody");
	  }
	else
	  principal = strdup (principal);
      }
      break;

    case AUTH_UNIX:

      log_msg (LOG_DEBUG, "AUTH_UNIX");

      if (securelevel > 1)
        {
          principal = strdup ("nobody");
          break;
        }
      else
	{
	  char *domain, *buffer = NULL;
	  size_t buflen = 0;
	  struct authunix_parms *creds =
	    (struct authunix_parms *) rqstp->rq_clntcred;

	  do {
	    errno = 0; /* Only to be sure */
	    buflen += 1024;
	    buffer = realloc (buffer, buflen);
	    domain = nis_domain_of_r (creds->aup_machname, buffer, buflen);
	  } while (domain == NULL && errno == ERANGE);

	  if (domain == NULL)
	    {
	      principal = strdup ("nobody");
	      log_msg (LOG_ERR, "couldn't convert LOCAL creds");
	      break;
	    }
	  if (domain[0] == '.' && domain[1] == '\0')
	    domain = nis_local_directory ();
	  else
	    {
	      int len = strlen (domain);

	      if (domain[len - 1] != '.')
		{
		  char *cp = alloca (len + 2);
		  domain = stpcpy (cp, domain);
		  *domain++ = '.';
		  *domain = '\0';
		  domain = cp;
		}
	    }

	  if (creds->aup_uid == 0)
	    {
	      char *netname = alloca (strlen (creds->aup_machname) + 2);
	      char *cp;

	      cp = stpcpy (netname, creds->aup_machname);
	      if (cp[-1] != '.')
		{
		  *cp++ = '.';
		  *cp = '\0';
		}
	      principal = strdup (netname);
	      break;
	    }
	  else
	    {
	      char netname[NIS_MAXNAMELEN + 1];

	      snprintf (netname, sizeof (netname), "%u", creds->aup_uid);
	      principal = nis_netid_to_princ (netname, domain, AUTH_UNIX);

	      if (principal == NULL)
		{
		  if (verbose)
		    log_msg (LOG_DEBUG, "warning: LOCAL cred lookup failed");
		  principal = strdup ("nobody");
		}
	      else
		principal = strdup (principal);
	    }
	}
      break;

    case AUTH_NONE:
      log_msg (LOG_DEBUG, "AUTH_NONE");

    default:
      principal = strdup ("nobody");
      break;
    }

  return principal;
}

/* Probe the cred table for the principal name that relates to a
   given RPC netname. */
static char *
nis_netid_to_princ (char *netid, char *domain, enum_t flavor)
{
  /* XXX */
  return NULL;
}
