/* how to validate a password. -jer 980524.1520
 * Yes, this is part of the slforum binary.
 */
/*
    slforum version 1.x - Simple chat room usable via telnet.
    Copyright (C) 1999 Jeremy D. Monin, Dan Chin, Matt Lochansky
	(forum@shadowlands.org).
    Based on jdkchat version 1.3, J.D.Koftinoff Software, Ltd. (GPL'd)

    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 of the License, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include <stdio.h>
#include <string.h>    /* for strncpy */

#define __USE_XOPEN
#define _XOPEN_VERSION 4
#include <unistd.h>    /* for crypt(3) */
#undef _XOPEN_VERSION
#undef __USE_XOPEN


#include <pwd.h>       /* for getpwnam(3) */
#include <sys/types.h> /* for getpwnam(3) */
#include <errno.h>  /* for errno, ENOMEM */

#include "slfprefs.h"  /* for USE_SHADOW or not */


#ifdef USE_SHADOW
#include <shadow.h>
#endif

#include <utmp.h>
#include "passwd.h"	/* decls for the routines below */

#ifndef UT_NAMESIZE	/* solaris doesn't have it, for example */
#define UT_NAMESIZE 8
#endif


/* partially initialize an entry right after accept(), to be
 * filled in after the rest of the info's available (at authent. time).
 */
void initlogentry (struct utmp *pentry,
		   char *fromhost, unsigned long hostaddr) {
#if defined(LINUX_UTMP) || defined(OSX_UTMP)
  strncpy(pentry->ut_host, fromhost, UT_HOSTSIZE);
  pentry->ut_host[UT_HOSTSIZE-1] = '\0';  /* jer added 980712.1248 */
#endif
#ifdef LINUX_UTMP
  pentry->ut_addr=hostaddr;
#endif
#ifdef SOLARIS_UTMP
  pentry->ut_type = USER_PROCESS;
#endif
}


/* We make up a port number, such as "slforum/913".
   there are (26*36) possible 'fake' ids.
 */
#define JER_NUM_FAKE_ID  936


/* temp for FC 9 */
time_t time(time_t *t);

/* initialize an entry and write it to wtmp file fname */
void writeloginentry (struct utmp *pentry, char *username,
		      const char *fname) {
  FILE *wtmpf;
  if (fname == NULL)
    return;

  wtmpf = fopen (fname, "a");
  if (wtmpf == NULL) {
    perror ("opening wtmp-style logfile");
    fprintf (stderr, "for file '%s'\n", fname );
    return;
  }
#if defined(LINUX_UTMP) || defined(SOLARIS_UTMP)
  pentry->ut_type=USER_PROCESS;
  pentry->ut_pid=getpid();
  strncpy(pentry->ut_user, username, UT_NAMESIZE);
#endif
  time(&(pentry->ut_time));


  /* time_t is long int; add bits to get a fake id. */
  {
    unsigned long tyme;
    int sum;
    char *fakechar;
    sum  = 0;
    tyme = (unsigned long int) pentry->ut_time;
    /* digest the timestamp */
    while (tyme > 0) {
      sum += (int)(tyme & 0xff);
      sum %= JER_NUM_FAKE_ID;
      tyme = tyme >> 8;
    }
    /* now mix in username in case same-second logins */
    fakechar = username;
    while (*fakechar != '\0') {
      sum += (int) *fakechar;
      sum %= JER_NUM_FAKE_ID;
      fakechar++;
    }

    /* now assign it to ut_line */
    sprintf (pentry->ut_line, UT_LINE_FMT, sum);
    /* and to ut_id */
#if defined(LINUX_UTMP) || defined(SOLARIS_UTMP)
    fakechar = pentry->ut_id;  /* a-z for first char of id */
    *fakechar = (char) (((int)'a') + sum/36);
    fakechar++;  /* second char of id */
    sum %= 36;
    if (sum < 26)  /* a-z */
      *fakechar = (char) (((int)'a') + sum);
    else	   /* 0-9 */
      *fakechar = (char) (((int)'0') - 26 + sum);
#endif
  }

  fwrite ( pentry, sizeof(struct utmp), 1, wtmpf );
  fclose (wtmpf);
}
#undef JER_NUM_FAKE_ID


/* write an entry (initialized by writeloginentry) to wtmp file fname */
void writelogoutentry (struct utmp *pentry, const char *fname) {
  FILE *wtmpf;
  if (fname == NULL)
    return;
  wtmpf = fopen (fname, "a");
  if (wtmpf == NULL) {
    perror ("opening wtmp-style logfile");
    fprintf (stderr, "for file '%s'\n", fname );
    return;
  }
  time(&(pentry->ut_time));
#if defined(LINUX_UTMP) || defined(SOLARIS_UTMP)
  memset(pentry->ut_user,0,UT_NAMESIZE);
#endif
  fwrite (pentry, sizeof(struct utmp), 1, wtmpf );
  fclose (wtmpf);
}


static struct slf_userinfo slfu;


/** for virtualization: all slforum code calls slf_, not real sys functions */
struct slf_userinfo *slf_getpwnam(const char * name) {
    struct passwd *pw = getpwnam (name);
    if (pw != NULL)
    {
	slfu.u = *pw;
	slfu.u_is_virtual = false;
	return &slfu;
    }
    return NULL;
}

/** for virtualization: all slforum code calls slf_, not real sys functions */
struct slf_userinfo *slf_getpwuid(uid_t uid) {
    struct passwd *pw = getpwuid (uid);
    if (pw != NULL)
    {
	slfu.u = *pw;
	slfu.u_is_virtual = false;
	return &slfu;
    }
    return NULL;
}


static gid_t admin_gid=0, users_gid=500;

/** for virtualization */
void slf_virtuser_set_admin_gid (gid_t admingid)
{
    admin_gid = admingid;
    if (admin_gid == users_gid)
	users_gid = admin_gid + 1;  /* any value other than admingid is fine */
}


#ifdef USE_PAM
#include "passwd-pam-inc.c"
#else
#include "passwd-unix-inc.c"
#endif


/* If USE_VIRTUSERS is 0,
 * this will just have some stub functions.
 */
#include "passwd-db-inc.c"


struct slf_userinfo *validate(char *uname, char *passwd, char *remotehost)
{
    struct slf_userinfo *retv = NULL;
    struct passwd *retpwd = NULL;

  /* initialize slfu as it would be for a non-virtuser (system-level user). */

    memset (&slfu, 0, sizeof(slfu));
    slfu.u_is_virtual = false;

  /* Now try to find 'em */

#ifdef USE_PAM
    retpwd = pam_validate (uname, passwd, remotehost);
    if (retpwd != NULL)  { slfu.u = *retpwd;  retv = &slfu; }
#else
    retpwd = unix_validate (uname, passwd);
    if (retpwd != NULL)  { slfu.u = *retpwd;  retv = &slfu; }
#endif 
#if USE_VIRTUSERS
    if (retv == NULL)
	retv = slf_virtuser_get_info (uname, passwd, false);
#endif
  return retv;
}




