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

/*
 * This CGI program is just some simple glue to invoke dacs_authenticate.
 * It is DACS-wrapped (unlike autologin(8)).
 * Configure an ACS_ERROR_HANDLER to redirect a user to this program if he
 * is denied access because he has not authenticated:
 *  ACS_ERROR_HANDLER \
 *     "NO_AUTH https://example.com/cgi-bin/dacs/dacs_autologin_ssl"
 * The jurisdiction must be configured for cert-based authentication.
 * In particular, it will probably be necessary to map the name extracted
 * from the cert into a valid DACS username.
 *
 * This program will redirect the user to dacs_authenticate, and if
 * authentication is successful, dacs_authenticate will redirect the user
 * back to the original request (if possible).
 * In this way, a user with a valid SSL client certificate can be automatically
 * authenticated, transparently and without doing anything.
 *
 * A USERNAME argument will be passed to dacs_authenticate.  By default,
 * it is the value of the SSL_CLIENT_S_DN_CN attribute.  Through the
 * CERT_NAME_ATTR argument, a different attribute name can be specified.
 * The DACS_ERROR_URL (automatically passed by dacs_acs), is passed to this
 * program and forwarded to dacs_authenticate.
 *
 * This all works because the client cert is sent automatically by the
 * browser and no user prompting is needed.  In more complicated situations,
 * this program will most likely not be very useful.
 */

#ifndef lint
static const char copyright[] =
"Copyright (c) 2003-2012\n\
Distributed Systems Software.  All rights reserved.";
static const char revid[] =
  "$Id: autologin_ssl.c 2594 2012-10-19 17:28:49Z brachman $";
#endif

#include "dacs.h"

static MAYBE_UNUSED char *log_module_name = "autologin_ssl";

int
main(int argc, char **argv)
{
  int i, redirect;
  char *invoke_dacs_auth, *auth_query_string, *auth_jurisdiction;
  char *cert_attr, *cert_name_attr, *errmsg, *redirect_url;
  char *request_method, *username;
  Ds ds;
  Jurisdiction *jp;
  Kwv *kwv;

  errmsg = NULL;

  if (dacs_init(DACS_WEB_SERVICE, &argc, &argv, &kwv, &errmsg) == -1) {

  fail:
	emit_html_header(stdout, NULL);
	if (errmsg != NULL) {
	  printf("%s\n", errmsg);
	  log_msg((LOG_ERROR_LEVEL, "%s", errmsg));
	}
	else
	  printf("A fatal error occurred\n");
	emit_html_trailer(stdout);

	exit(1);
  }

  if (!dacs_is_https_request()) {
	errmsg = "This web service must be invoked via SSL";
	goto fail;
  }

  auth_jurisdiction = NULL;
  cert_name_attr = "SSL_CLIENT_S_DN_CN";
  redirect = 1;
  redirect_url = NULL;
  request_method = NULL;
  username = NULL;

  for (i = 0; i < kwv->nused; i++) {
	if (streq(kwv->pairs[i]->name, "DACS_ERROR_URL"))
	  redirect_url = kwv->pairs[i]->val;
	else if (streq(kwv->pairs[i]->name, "CERT_NAME_ATTR"))
	  cert_name_attr = kwv->pairs[i]->val;
    else if (streq(kwv->pairs[i]->name, "NOREDIRECT"))
      redirect = 0;
    else if (streq(kwv->pairs[i]->name, "AUTH_JURISDICTION"))
      auth_jurisdiction = kwv->pairs[i]->val;
    else if (streq(kwv->pairs[i]->name, "DACS_REQUEST_METHOD"))
      request_method = kwv->pairs[i]->val;
  }

  if (*cert_name_attr != '\0') {
	if ((cert_attr = getenv(cert_name_attr)) == NULL) {
	  errmsg = ds_xprintf("Could not find cert attribute \"%s\"",
						  cert_name_attr);
	  goto fail;
	}
	username = url_encode(cert_attr, 0);
  }
  else
	username = "";

  if (auth_jurisdiction == NULL)
	auth_jurisdiction = conf_val(CONF_JURISDICTION_NAME);

  if (get_jurisdiction_meta(auth_jurisdiction, &jp) == -1
      || jp->dacs_url == NULL || *jp->dacs_url == '\0') {
	errmsg = "Can't find jurisdiction?";
	goto fail;
  }
  if (strprefix(jp->dacs_url, "https") == NULL) {
	errmsg = "dacs_authenticate must also be invoked via SSL";
	goto fail;
  }

  /*
   * Construct a QUERY_STRING for dacs_authenticate.
   * XXX do something with an unusable DACS_REQUEST_METHOD
   */
  ds_init(&ds);
  if (redirect_url != NULL && redirect) {
    ds_asprintf(&ds, "USERNAME=%s", username);
	ds_asprintf(&ds, "&DACS_AUTH_SUCCESS_HANDLER=%s", redirect_url);
	ds_asprintf(&ds, "&DACS_JURISDICTION=%s", auth_jurisdiction);
	ds_asprintf(&ds, "&DACS_BROWSER=1");
  } else {
    ds_asprintf(&ds, "USERNAME=%s", username);
	ds_asprintf(&ds, "&DACS_JURISDICTION=%s", auth_jurisdiction);
	ds_asprintf(&ds, "&DACS_BROWSER=1");
  }
  auth_query_string = ds_buf(&ds);

  /* Construct the command to run dacs_authenticate. */
  invoke_dacs_auth = ds_xprintf("%s/dacs_authenticate?%s",
								jp->dacs_url, auth_query_string);

  log_msg((LOG_TRACE_LEVEL, "Redirecting to: %s", invoke_dacs_auth));

  if (emit_http_header_redirect(stdout, invoke_dacs_auth) == -1) {
	errmsg = "Could not redirect?";
	goto fail;
  }

  exit(0);
}
