/*
 * Copyright (c) 2003-2011
 * Distributed Systems Software.  All rights reserved.
 * See the file LICENSE for redistribution information.
 */

/*
 * Pseudo one-time passwords
 * This module is used in conjunction with the auth_grid utility.
 *
 * Inputs: USERNAME, from which a grid can be obtained, a challenge
 * created by auth_grid relative to that grid and passed as AUXILILARY,
 * and a user's response to that challenge (PASSWORD).
 *
 * All this module does is validate the argument and verify that PASSWORD
 * is the correct answer to the challenge.
 */

#ifndef lint
static const char copyright[] =
"Copyright (c) 2003-2011\n\
Distributed Systems Software.  All rights reserved.";
static const char revid[] =
  "$Id: local_grid_auth.c 2528 2011-09-23 21:54:05Z brachman $";
#endif

#include "dacs.h"

static const char *log_module_name = "local_grid_auth";

static unsigned int grid_lifetime_secs = 0;
static unsigned int challenge_lifetime_secs = 0;

/*
 * Test if USERNAME's reply (PASSWORD) to a grid challenge (encoded within
 * AUX) is correct.
 * Return 0 if authentication succeeds, -1 otherwise.
 */
int
local_grid_auth(char *username, char *password, char *aux)
{
  int st;
  Auth_grid *grid;
  Dsvec *challenge;
  Proc_lock *lock;
  
  if ((lock = proc_lock_create(PROC_LOCK_GRID)) == NULL) {
	log_msg((LOG_ERROR_LEVEL, "Cannot obtain lock"));
	return(-1);
  }

  st = -1;
  if ((grid = auth_grid_get("auth_grid", username)) == NULL) {
	log_msg((LOG_ERROR_LEVEL,
			 "Could not find grid for user \"%s\"", username));
	goto done;
  }

  if (!grid->enabled) {
	log_msg((LOG_ERROR_LEVEL,
			 "Account for user \"%s\" is disabled", username));
	goto done;
  }

  if ((challenge = auth_grid_decrypt_challenge(aux, challenge_lifetime_secs))
	  == NULL) {
	log_msg((LOG_ERROR_LEVEL, "Failed to decrypt challenge"));
	goto done;
  }

  if ((st = auth_grid_verify(grid, challenge, password, grid_lifetime_secs))
	  == 0)
	log_msg((LOG_DEBUG_LEVEL, "Grid verification succeeded"));
  else
	log_msg((LOG_ERROR_LEVEL, "Grid verification failed"));

 done:
  proc_lock_delete(lock);

  return(st);
}

#ifdef PROG
int
main(int argc, char **argv)
{
  int emitted_dtd, i;
  char *errmsg, *jurisdiction, *username, *password, *aux;
  Auth_reply_ok ok;
  Kwv *kwv;

  emitted_dtd = 0;
  errmsg = "internal";
  username = password = aux = jurisdiction = NULL;

  if (dacs_init(DACS_LOCAL_SERVICE, &argc, &argv, &kwv, &errmsg) == -1) {
	/* If we fail here, we may not have a DTD with which to reply... */
  fail:
	if (password != NULL)
	  strzap(password);
	if (aux != NULL)
	  strzap(aux);
	if (emitted_dtd) {
	  printf("%s\n", make_xml_auth_reply_failed(NULL, NULL));
	  emit_xml_trailer(stdout);
	}
	if (errmsg != NULL)
	  log_msg((LOG_ERROR_LEVEL, "Failed: reason=%s", errmsg));

	exit(1);
  }

  /* This must go after initialization. */
  emitted_dtd = emit_xml_header(stdout, "auth_reply");

  if (argc > 1) {
	errmsg = "Usage: unrecognized parameter";
	goto fail;
  }

  for (i = 0; i < kwv->nused; i++) {
	if (streq(kwv->pairs[i]->name, "USERNAME") && username == NULL)
	  username = kwv->pairs[i]->val;
	else if (streq(kwv->pairs[i]->name, "PASSWORD") && password == NULL)
	  password = kwv->pairs[i]->val;
	else if (streq(kwv->pairs[i]->name, "AUXILIARY") && aux == NULL)
	  aux = kwv->pairs[i]->val;
	else if (streq(kwv->pairs[i]->name, "DACS_JURISDICTION")
			 && jurisdiction == NULL)
	  jurisdiction = kwv->pairs[i]->val;
	else if (streq(kwv->pairs[i]->name, "DACS_VERSION"))
	  ;
	else if (streq(kwv->pairs[i]->name, "AUTH_GRID_CHALLENGE_SECS")
			 && challenge_lifetime_secs == 0) {
	  if (strnum(kwv->pairs[i]->val, STRNUM_UI, &challenge_lifetime_secs)
		  == -1) {
		errmsg = "Invalid AUTH_GRID_CHALLENGE_SECS argument";
		goto fail;
	  }
	}
	else if (streq(kwv->pairs[i]->name, "AUTH_GRID_LIFETIME_SECS")
			 && grid_lifetime_secs == 0) {
	  if (strnum(kwv->pairs[i]->val, STRNUM_UI, &grid_lifetime_secs) == -1) {
		errmsg = "Invalid AUTH_GRID_LIFETIME_SECS argument";
		goto fail;
	  }
	}
	else
	  log_msg((LOG_TRACE_LEVEL, "Parameter: '%s'", kwv->pairs[i]->name));
  }

  if (challenge_lifetime_secs == 0)
	challenge_lifetime_secs = AUTH_GRID_CHALLENGE_SECS;
  if (grid_lifetime_secs == 0)
	grid_lifetime_secs = AUTH_GRID_LIFETIME_SECS;

  /* Verify that we're truly responsible for DACS_JURISDICTION */
  if (dacs_verify_jurisdiction(jurisdiction) == -1) {
	errmsg = "Missing or incorrect DACS_JURISDICTION";
	goto fail;
  }

  if (username == NULL || password == NULL || aux == NULL
	  || local_grid_auth(username, password, aux) == -1) {
	errmsg = "Username/Password/Aux incorrect";
    goto fail;
  }

  if (password != NULL)
	strzap(password);
  if (aux != NULL)
	strzap(aux);

  ok.username = username;
  /* If this wasn't specified, dacs_authenticate will use the default. */
  ok.lifetime = kwv_lookup_value(kwv, "CREDENTIALS_LIFETIME_SECS");
  ok.roles_reply = NULL;
  printf("%s\n", make_xml_auth_reply_ok(&ok));

  emit_xml_trailer(stdout);
  exit(0);
}
#endif
