/*
 * Copyright 2001 by Lionel Schwarz, Computing Center IN2P3
 */

/*#ifdef HAVE_UNISTD_H*/
#include <unistd.h>
/*#endif*/
#include <syslog.h>
#include <errno.h>

#include "gfw-misc.h"
#include "gfw.h"

#ifndef gss_nt_service_name
#define gss_nt_service_name GSS_C_NT_HOSTBASED_SERVICE
#endif

/*#ifdef USE_STRING_H*/
#include <string.h>
/*#else
#include <strings.h>
#endif*/

FILE *log;
int debug_gfw=0;

/*extern int verbose;*/

/*
 * Function: server_acquire_creds
 *
 * Purpose: imports a service name and acquires credentials for it
 *
 * Arguments:
 *
 * 	service_name	(r) the ASCII service name
 * 	server_creds	(w) the GSS-API service credentials
 *
 * Returns: 0 on success, -1 on failure
 *
 * Effects:
 *
 * The service name is imported with gss_import_name, and service
 * credentials are acquired with gss_acquire_cred.  If either opertion
 * fails, an error message is displayed and -1 is returned; otherwise,
 * 0 is returned.
 */
int server_acquire_creds(service_name, server_creds)
     char *service_name;
     gss_cred_id_t *server_creds;
{
     gss_buffer_desc name_buf;
     gss_name_t server_name = GSS_C_NO_NAME;
     OM_uint32 maj_stat, min_stat;

	 if (service_name) {
if(debug_gfw) fprintf(display_file,"service_name=%s\n",service_name) ;
		 name_buf.value = service_name;
		 name_buf.length = strlen(name_buf.value) + 1;
		 maj_stat = gss_import_name(&min_stat, &name_buf, 
				(gss_OID) gss_nt_service_name, &server_name);
		if (maj_stat != GSS_S_COMPLETE) {
			display_status("importing name", maj_stat, min_stat);
fflush(display_file);
	  		return -1;
		}
	 }

     maj_stat = gss_acquire_cred(&min_stat, server_name, 0,
				 GSS_C_NULL_OID_SET, GSS_C_ACCEPT,
				 server_creds, NULL, NULL);
     if (maj_stat != GSS_S_COMPLETE) {
	  display_status("acquiring credentials", maj_stat, min_stat);
fflush(display_file);
	  return -1;
     }

     (void) gss_release_name(&min_stat, &server_name);

     return 0;
}

/*
 * Function: server_establish_context
 *
 * Purpose: establishses a GSS-API context as a specified service with
 * an incoming client, and returns the context handle and associated
 * client name
 *
 * Arguments:
 *
 * 	is		(r) input stream socket
 *	os		(r) output stream socket
 * 	service_creds	(r) server credentials, from gss_acquire_cred
 * 	context		(w) the established GSS-API context
 * 	client_name	(w) the client's ASCII name
 *	ret_flags	(w)	the flags returned
 *
 * Returns: 0 on success, -1 on failure
 *
 * Effects:
 *
 * Any valid client request is accepted.  If a context is established,
 * its handle is returned in context and the client name is returned
 * in client_name and 0 is returned.  If unsuccessful, an error
 * message is displayed and -1 is returned.
 */
int server_establish_context(is, os, server_creds, context, client_name, ret_flags)
     int is;
	 int os;
     gss_cred_id_t server_creds;
     gss_ctx_id_t *context;
     gss_buffer_t client_name;
     OM_uint32 *ret_flags;

{
     gss_buffer_desc send_tok, recv_tok;
     gss_name_t client;
     gss_OID doid;
     OM_uint32 maj_stat, min_stat;
     gss_buffer_desc	oid_name;

     *context = GSS_C_NO_CONTEXT;
     
if (debug_gfw) fprintf(display_file, "looping...\n");
     do {
if (debug_gfw) fprintf(display_file, "receiving token...\n");
if (debug_gfw) fflush(display_file);
	  if (recv_token(is, &recv_tok) < 0)
	       return -1;

if (debug_gfw) fprintf(display_file, "Received token (size=%d): \n", recv_tok.length);

if(debug_gfw) fprintf(display_file, "calling gss_accept_sec_context...\n");
	  maj_stat =
	       gss_accept_sec_context(&min_stat,
				      context,
				      server_creds,
				      &recv_tok,
				      GSS_C_NO_CHANNEL_BINDINGS,
				      &client,
				      &doid,
				      &send_tok,
				      ret_flags,
				      NULL, 	/* ignore time_rec */
				      NULL); 	/* ignore del_cred_handle */

	  if (maj_stat!=GSS_S_COMPLETE && maj_stat!=GSS_S_CONTINUE_NEEDED) {
	       display_status("accepting context", maj_stat, min_stat);
	       (void) gss_release_buffer(&min_stat, &recv_tok);
	       return -1;
	  }

	  (void) gss_release_buffer(&min_stat, &recv_tok);

	  if (send_tok.length != 0) {
if (debug_gfw) fprintf(display_file, "Sending accept_sec_context token (size=%d):\n", send_tok.length);
	       if (send_token(os, &send_tok) < 0) {
		    fprintf(display_file, "failure sending token\n");
		    return -1;
	       }
if (debug_gfw) fprintf(display_file, "token sent\n");

	       (void) gss_release_buffer(&min_stat, &send_tok);
	  }

     } while (maj_stat & GSS_S_CONTINUE_NEEDED);
if (debug_gfw) fprintf(display_file, "COMPLETE\n");
     /* display the flags */
/*     display_ctx_flags(*ret_flags);

*/
     maj_stat = gss_display_name(&min_stat, client, client_name, &doid);
     if (maj_stat != GSS_S_COMPLETE) {
	  display_status("displaying name", maj_stat, min_stat);
	  return -1;
     }
     maj_stat = gss_release_name(&min_stat, &client);
     if (maj_stat != GSS_S_COMPLETE) {
	  display_status("releasing name", maj_stat, min_stat);
	  return -1;
     }

     return 0;

}


/*
 * Function: sign_server
 *
 * Purpose: Performs the "sign" service.
 *
 * Arguments:
 *
 * 	is		(r) input stream socket
 *	os		(r) output stream socket
 * 	service_creds	(r) server credentials
 * 
 * Returns: -1 on error
 *
 * Effects:
 *
 * sign_server establishes a context
 *
 * If any error occurs, -1 is returned.
 */
int sign_server(is, os, server_creds)
     int is;
	 int os;
     gss_cred_id_t server_creds;
{
     gss_buffer_desc client_name, logon_buf, out_buf;
     gss_ctx_id_t context;
     OM_uint32 maj_stat, min_stat;
     int i;
     int conf_state = 0;
     int ret_flags = 0;
     char	*cp;
     gss_name_t		src_name, targ_name;
     gss_buffer_desc	sname, tname;
     OM_uint32		lifetime;
     gss_OID		mechanism, name_type;
     int		is_local;
     OM_uint32		context_flags;
     int		is_open;
	 char 		*userid;
	int state;
     
     /* Establish a context with the client */
if (debug_gfw) fprintf(display_file, "establishing context...\n");
fflush(display_file);
     if (server_establish_context(is, os, server_creds, &context,
				  &client_name, &ret_flags) < 0)
	return(-1);
	  
     /* Get context information */
     maj_stat = gss_inquire_context(&min_stat, context,
				    &src_name, &targ_name, &lifetime,
				    &mechanism, &context_flags,
				    &is_local,
				    &is_open);
     if (maj_stat != GSS_S_COMPLETE) {
	 display_status("inquiring context", maj_stat, min_stat);
	 return -1;
     }

     maj_stat = gss_display_name(&min_stat, src_name, &sname,
				 &name_type);
     if (maj_stat != GSS_S_COMPLETE) {
	 display_status("displaying source name", maj_stat, min_stat);
	 return -1;
     }
     maj_stat = gss_display_name(&min_stat, targ_name, &tname,
				 (gss_OID *) NULL);
     if (maj_stat != GSS_S_COMPLETE) {
	 display_status("displaying target name", maj_stat, min_stat);
	 return -1;
     }
/*     fprintf(stderr, "\"%.*s\" to \"%.*s\", lifetime %d, flags %x, %s, %s\n",
	     (int) sname.length, (char *) sname.value,
	     (int) tname.length, (char *) tname.value, lifetime,
	     context_flags,
	     (is_local) ? "locally initiated" : "remotely initiated",
	     (is_open) ? "open" : "closed");*/

	if (globus_gss_assist_gridmap((char *) sname.value, &userid) != 0) {
		(void) gss_release_name(&min_stat, &src_name);
		(void) gss_release_name(&min_stat, &targ_name);
		(void) gss_release_buffer(&min_stat, &sname);
		(void) gss_release_buffer(&min_stat, &tname);

if (debug_gfw) fprintf(display_file,"Accepted connection: \"%.*s\"\n",
	    (int) client_name.length, (char *) client_name.value);
		(void) gss_release_buffer(&min_stat, &client_name);

     /* Delete context */
     	maj_stat = gss_delete_sec_context(&min_stat, &context, NULL);
		if (maj_stat != GSS_S_COMPLETE) {
			display_status("deleting context", maj_stat, min_stat);
			return(-1);
		}
		return(-1);
	}

	 
     (void) gss_release_name(&min_stat, &src_name);
     (void) gss_release_name(&min_stat, &targ_name);
     (void) gss_release_buffer(&min_stat, &sname);
     (void) gss_release_buffer(&min_stat, &tname);

if (debug_gfw) fprintf(display_file,"Accepted connection: \"%.*s\"\n",
	    (int) client_name.length, (char *) client_name.value);
     (void) gss_release_buffer(&min_stat, &client_name);

     /* Delete context */
     maj_stat = gss_delete_sec_context(&min_stat, &context, NULL);
     if (maj_stat != GSS_S_COMPLETE) {
	display_status("deleting context", maj_stat, min_stat);
	return(-1);
     }

     /*fflush(log);*/

     return(0);
}

/*
 * Function: gfw_server_auth
 *
 * Purpose: Performs the authentication on a specified service
 *			to allow a client connected by the TCP connection
 *			to authenticate to this server for this service
 *
 * Arguments:
 *
 * 	is		(r) input stream socket
 *	os		(r) output stream socket
 * 	service_name	(r) the ASCII service name
 *	logmessage		(w) the message written int	case of failure
 * 
 * Returns: -1 on error, 0 else
 */
int gfw_server_auth(is, os, service_name, logmessage)
     int is;
     int os;
	 char *service_name;
	 char *logmessage;
{
     gss_cred_id_t server_creds;
     OM_uint32 min_stat;
	 char *logfilename;
     log = stdout;
/*     display_file = stdout;*/
/*     sprintf(logfilename,"/tmp/gfw-server.log") ;*/
if (debug_gfw) display_file = fopen("/tmp/gfw-server.log","w") ;
	if (server_acquire_creds(service_name, &server_creds) < 0) {
if (debug_gfw) fprintf(display_file,"server_acquire_creds():%s\n",strerror(errno)) ;
	logmessage = strerror(errno);
	 return -1;
	}
	
	if (sign_server(is, os, server_creds) < 0) {
if (debug_gfw) fprintf(display_file,"sign_server():%s\n", strerror(errno));
		logmessage = strerror(errno);
		return -1;
	}

	(void) gss_release_cred(&min_stat, &server_creds);

	return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1