/* This file is part of GNU Pies.
   Copyright (C) 2015-2025 Sergey Poznyakoff

   GNU Pies 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 3, or (at your option)
   any later version.

   GNU Pies 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 GNU Pies.  If not, see <http://www.gnu.org/licenses/>. */

#include "ident.h"
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#if defined(HAVE_CRYPT_H)
# include <crypt.h>
#endif
#if defined(HAVE_SHADOW_H)
# include <shadow.h>
#endif

struct system_identity_data
{
  gid_t gid;
};

static int
system_authenticate (pies_identity_provider_t pr, pies_identity_t id,
		     char const *passwd)
{
  struct passwd *pwd;
  char *encrypted_pass = NULL;
  
  pwd = getpwnam (id->username);
  if (!pwd)
    return -1;

  encrypted_pass = pwd->pw_passwd;
  
#if defined(HAVE_SHADOW_H)
  {
    struct spwd *sp;
    if ((sp = getspnam (id->username)) != NULL)
      {
	if (sp->sp_expire > 0 && time (NULL) > sp->sp_expire * 86400)
	  return -1;
	encrypted_pass = sp->sp_pwdp;
      }
  }
#endif

  if (strcmp (crypt (passwd, encrypted_pass), encrypted_pass) == 0)
    {
      struct system_identity_data *data = malloc (sizeof (*data));
      if (!data)
	return -1;
      data->gid = pwd->pw_gid;
      id->data = data;
      return 0;
    }
  return -1;
}

static int
system_is_group_member (pies_identity_provider_t provider,
			pies_identity_t id, char * const * groups)
{
  struct system_identity_data *data = id->data;
  struct group *gr;
  int result = 0;
  
  setgrent ();
  while (!result && (gr = getgrent ()))
    {
      if (is_array_member (groups, gr->gr_name))
	{
	  if (gr->gr_gid == data->gid)
	    result = 1;
	  else
	    result = is_array_member (gr->gr_mem, id->username);
	}
    }
  endgrent ();
  return result;
}

static void
system_destroy_identity (pies_identity_provider_t p, pies_identity_t id)
{
  if (id->data)
    free (id->data);
}

static void
confhelp (void)
{
  static struct grecs_keyword kw[] = {
    { "type", "'system", "Set mechanism type", grecs_type_null },
    { NULL }
  };

  static struct grecs_keyword top[] = {
    { "identity-provider", "name: string",
      "Configuration for system identity provider",
      grecs_type_section, GRECS_INAC, NULL, 0, NULL, NULL,
      kw },
    { NULL }
  };
  grecs_print_statement_array (top, 1, 0, stdout);
}

struct pies_identity_mechanism system_identity_mechanism = {
  "system",
  system_authenticate,  
  system_is_group_member,
  system_destroy_identity,
  NULL,
  confhelp
};

