/***************************************************************************
 *
 * Copyright (c) 2000, 2001 BalaBit IT Ltd, Budapest, Hungary
 *
 * 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.
 *
 * $Id: tisauth.c,v 1.4.2.2 2001/09/19 19:22:17 bazsi Exp $
 *
 ***************************************************************************/

#include <zorp/tisauth.h>
#include <time.h>
#include <sys/poll.h>
#include <string.h>

#define MAX_TIS_ATTEMPTS 3

struct _ZTISContext
{
  int connfd;
  ZSockAddr *server;
};

ZTISContext *
z_tis_new(ZSockAddr *sa)
{
  ZTISContext *self;
  
  self = g_new0(ZTISContext, 1);
  self->connfd = -1;
  self->server = z_sockaddr_ref(sa);
  return self;
}

void
z_tis_close(ZTISContext *ctx)
{
  if (ctx->connfd != -1)
    {
      close(ctx->connfd);
      ctx->connfd = -1;
    }
}

gboolean
z_tis_connect(ZTISContext *ctx)
{
  char line[128];
  int fd;

  z_tis_close(ctx);
  
  fd = socket(ctx->server->sa.sa_family, SOCK_STREAM, 0);
  if (fd < 0)
    return FALSE;

  if (z_connect(fd, ctx->server) != G_IO_ERROR_NONE)
    {
      close(fd);
      return FALSE;
    }

  /* grab greeting line */
  if (recv(fd, line, sizeof(line), 0) < 0)
    {
      close(fd);
      return FALSE;
    }
  ctx->connfd = fd;
  return TRUE;
}
#if 0
ZTISContext *
z_tis_connect_multi(struct addrinfo *addrs)
{
  struct addrinfo *p;
  ZTISContext *tis;

  for (p = addrs; p; p = p->ai_next)
    {
      tis = z_tis_connect(p->ai_addr, p->ai_addrlen);
      if (tis)
	return tis;
    }
  return NULL;
}
#endif

void
z_tis_destroy(ZTISContext *ctx)
{
  if (ctx)
    {
      close(ctx->connfd);
      z_sockaddr_unref(ctx->server);
      g_free(ctx);
    }
}

gboolean
z_tis_authenticate(ZTISContext *ctx, gchar *challenge, gint challenge_size, gchar *user)
{
  gchar line[128], *p;
  gint length;
  gint attempts = 0;

  do
    {
      if (ctx->connfd == -1 && !z_tis_connect(ctx))
        {
          return FALSE;
        }

      g_snprintf(line, 120, "authenticate \"%.32s\"\n", user);
      if (send(ctx->connfd, line, strlen(line), 0) >= 0)
        {
          if ((length = recv(ctx->connfd, line, sizeof(line) - 1, 0)) > 0)
            {
              line[length] = 0;
              if (line[length - 1] == '\n')
		line[length - 1] = 0;
	      p = line;
	      while (*p == '\n') p++;
	      if (strncmp(p, "challenge", 9) == 0)
		{
		  strncpy(challenge, line + 10, challenge_size);
		}
	      else if (strncmp(p, "password", 8) == 0)
		{
		  challenge[0] = 0;
		}
	      else
		{
		  return FALSE;
		}
	      return TRUE;
	    }
	  else
	    {
	      z_tis_close(ctx);
	    }
        }
      else
	{
	  z_tis_close(ctx);
	}
      attempts++;
    }
  while (attempts < MAX_TIS_ATTEMPTS);
  return FALSE;
}

gboolean
z_tis_challenge(ZTISContext *ctx, gchar *challenge, gint challenge_size, gchar *user)
{
  char line[128], *p;
  int length;
  gint attempts = 0;

  do
    {
      if (ctx->connfd == -1 && !z_tis_connect(ctx))
        {
          return FALSE;
        }

      g_snprintf(line, 120, "challenge \"%.32s\"\n", user);
      if (send(ctx->connfd, line, strlen(line), 0) >= 0)
	{
	  if ((length = recv(ctx->connfd, line, sizeof(line) - 1, 0)) > 0)
	    {
	      line[length] = 0;
	      if (line[length - 1] == '\n')
		line[length - 1] = 0;
	      p = line;
	      while (*p == '\n') p++;
	      if (strncmp(p, "challenge", 9) == 0)
		{
		  char *begin;
		  
		  p = line + 10;
		  if (*p == '"') p++;
		  begin = p;
		  p = strchr(p, '"');
		  if (p) *p = 0;
		  strncpy(challenge, begin, challenge_size);
		}
	      else if (strncmp(p, "password", 8) == 0)
		{
		  challenge[0] = 0;
		}
	      else
		{
		  return FALSE;
		}
	      return TRUE;
	    }
	  else
	    {
	      z_tis_close(ctx);
	    }
	}
      else
	{
	  z_tis_close(ctx);
	}
      attempts++;
    }
  while (attempts < MAX_TIS_ATTEMPTS);
  return FALSE;
}

int
z_tis_response(ZTISContext *ctx, char *response)
{
  char line[128];
  gint length;

  g_snprintf(line, 120, "response \"%.64s\"\n", response);
  if (send(ctx->connfd, line, strlen(line), 0) < 0)
    return 0;
  if ((length = recv(ctx->connfd, line, sizeof(line) - 1, 0)) < 0)
    return 0;
  line[length] = 0;
  if (strncmp(line, "ok", 2) == 0)
    {
      return 1;
    }
  return 0;
}

int 
z_tis_respchal(ZTISContext *ctx, char *username, char *challenge, char *response)
{
  char line[256];
  int length, attempts = 0;

  do
    {
      if (ctx->connfd == -1 && !z_tis_connect(ctx))
        {
          return FALSE;
        }

      g_snprintf(line, 120, "respchal \"%.64s\" \"%.64s\" \"%.64s\"\n", username, challenge, response);
      if (send(ctx->connfd, line, strlen(line), 0) > 0)
	{
	  if ((length = recv(ctx->connfd, line, sizeof(line) - 1, 0)) > 0)
	    {
	      line[length] = 0;
	      if (strncmp(line, "ok", 2) == 0)
		{
		  return 1;
		}
	      else
		{
		  return 0;
		}
	    }
	  else
	    {
	      z_tis_close(ctx);
	    }
	}
      else
	{
	  z_tis_close(ctx);
	}
      attempts++;
    }
  while (attempts < MAX_TIS_ATTEMPTS);
  return 0;
  
}

char *
z_tis_fake_challenge(void)
{
  char challenge[9];
  unsigned long rnd;

  rnd = time(NULL) ^ getpid();
  g_snprintf(challenge, sizeof(challenge), "%ld", rnd);
  return strdup(challenge);
}
