*** stunnel-4.26/src/Makefile.am	2008-09-20 22:32:18.000000000 +0200
--- stunnel-4.26-identprop/src/Makefile.am	2008-12-18 10:23:57.000000000 +0100
***************
*** 3,9 ****
  # File lists
  
  common_headers = common.h prototypes.h
! common_sources = file.c client.c log.c options.c protocol.c network.c resolver.c ssl.c ctx.c verify.c sthreads.c stunnel.c
  unix_sources = pty.c libwrap.c
  shared_sources = env.c
  win32_sources = gui.c resources.h resources.rc stunnel.ico
--- 3,9 ----
  # File lists
  
  common_headers = common.h prototypes.h
! common_sources = file.c client.c log.c options.c protocol.c network.c resolver.c ssl.c ctx.c verify.c sthreads.c stunnel.c ident.c
  unix_sources = pty.c libwrap.c
  shared_sources = env.c
  win32_sources = gui.c resources.h resources.rc stunnel.ico
*** stunnel-4.26/src/stunnel.c	2008-06-21 23:32:45.000000000 +0200
--- stunnel-4.26-identprop/src/stunnel.c	2008-12-18 10:23:57.000000000 +0100
***************
*** 130,135 ****
--- 130,136 ----
      SOCKADDR_UNION addr;
      s_poll_set fds;
      LOCAL_OPTIONS *opt;
+     int fd_ident=0,fd_ident_admin=0;
      get_limits();
      s_poll_zero(&fds);
  #if !defined(USE_WIN32) && !defined(USE_OS2)
***************
*** 171,176 ****
--- 172,186 ----
  #endif
          s_poll_add(&fds, opt->fd, 1, 0);
      }
+     
+     if (options.option.ident_server) {
+     	if ((fd_ident=create_ident_socket())>0) {
+     		s_poll_add( &fds, fd_ident, 1, 0);
+    			if (options.ident_admin.addr[0].in.sin_port!=0)
+ 				if ((fd_ident_admin=create_ident_admin_socket())>0)
+ 					s_poll_add( &fds, fd_ident_admin, 1, 0);
+ 		}
+     }
  
  #if !defined (USE_WIN32) && !defined (__vms) && !defined(USE_OS2)
      if(!(options.option.foreground))
***************
*** 199,205 ****
              for(opt=local_options.next; opt; opt=opt->next)
                  if(s_poll_canread(&fds, opt->fd))
                      accept_connection(opt);
! 
          }
      }
      s_log(LOG_ERR, "INTERNAL ERROR: End of infinite loop 8-)");
--- 209,218 ----
              for(opt=local_options.next; opt; opt=opt->next)
                  if(s_poll_canread(&fds, opt->fd))
                      accept_connection(opt);
!             if((fd_ident!=0)&&(s_poll_canread(&fds, fd_ident)))
!             	process_ident_request(fd_ident);
!             if((fd_ident_admin!=0)&&(s_poll_canread(&fds, fd_ident_admin)))
!             	process_ident_admin_request(fd_ident_admin);
          }
      }
      s_log(LOG_ERR, "INTERNAL ERROR: End of infinite loop 8-)");
*** stunnel-4.26/src/prototypes.h	2008-06-21 23:15:14.000000000 +0200
--- stunnel-4.26-identprop/src/prototypes.h	2008-12-18 10:23:57.000000000 +0100
***************
*** 145,150 ****
--- 145,155 ----
      char *win32_service;
  #endif
  
+ 		/* Ident Server date for ident.c */
+ 	SOCKADDR_LIST ident_bind;  /* listening address for ident server */
+ 	SOCKADDR_LIST ident_admin;  /* listening address for administrative ident server */
+ 	char *ident_admin_allowed; /* machine which can ask administrative ident requests */
+ 
          /* logging-support data for log.c */
      int debug_level;                              /* debug level for logging */
  #ifndef USE_WIN32
***************
*** 160,165 ****
--- 165,171 ----
  #else /* !USE_WIN32 */
          unsigned int foreground:1;
          unsigned int syslog:1;
+         unsigned int ident_server:1;  /* set if at least one service ask for */
  #endif
  #ifdef USE_FIPS
          unsigned int fips:1;                       /* enable FIPS 140-2 mode */
***************
*** 182,188 ****
  #ifndef USE_FORK
      int stack_size;                            /* stack size for this thread */
  #endif
! 
          /* service-specific data for ctx.c */
      char *ca_dir;                              /* directory for hashed certs */
      char *ca_file;                       /* file containing bunches of certs */
--- 188,195 ----
  #ifndef USE_FORK
      int stack_size;                            /* stack size for this thread */
  #endif
! 	struct _CLI *connection_list;                       /* for ident purpose */
! 	
          /* service-specific data for ctx.c */
      char *ca_dir;                              /* directory for hashed certs */
      char *ca_file;                       /* file containing bunches of certs */
***************
*** 209,214 ****
--- 216,222 ----
      SOCKADDR_LIST source_addr;
      char *username;
      char *remote_address;
+     unsigned int ident_fields;
      int timeout_busy; /* Maximum waiting for data time */
      int timeout_close; /* Maximum close_notify time */
      int timeout_connect; /* Maximum connect() time */
***************
*** 229,234 ****
--- 237,243 ----
          unsigned int accept:1;
          unsigned int remote:1;
          unsigned int retry:1; /* loop remote+program */
+         unsigned int ident:1;
  #ifndef USE_WIN32
          unsigned int program:1;
          unsigned int pty:1;
***************
*** 312,319 ****
      int is_socket; /* File descriptor is a socket */
  } FD;
  
! typedef struct {
      LOCAL_OPTIONS *opt;
      char accepted_address[IPLEN]; /* text */
      SOCKADDR_LIST peer_addr; /* Peer address */
      FD local_rfd, local_wfd; /* Read and write local descriptors */
--- 321,332 ----
      int is_socket; /* File descriptor is a socket */
  } FD;
  
! typedef unsigned int Port;
! 
! typedef struct _CLI {
      LOCAL_OPTIONS *opt;
+     struct _CLI *next;
+     Port local_port;
      char accepted_address[IPLEN]; /* text */
      SOCKADDR_LIST peer_addr; /* Peer address */
      FD local_rfd, local_wfd; /* Read and write local descriptors */
***************
*** 377,383 ****
  
  typedef enum {
      CRIT_KEYGEN, CRIT_INET, CRIT_CLIENTS, CRIT_WIN_LOG, CRIT_SESSION,
!     CRIT_LIBWRAP, CRIT_SSL, CRIT_SECTIONS
  } SECTION_CODE;
  
  void enter_critical_section(SECTION_CODE);
--- 390,396 ----
  
  typedef enum {
      CRIT_KEYGEN, CRIT_INET, CRIT_CLIENTS, CRIT_WIN_LOG, CRIT_SESSION,
!     CRIT_IDENT, CRIT_LIBWRAP, CRIT_SSL, CRIT_SECTIONS
  } SECTION_CODE;
  
  void enter_critical_section(SECTION_CODE);
***************
*** 463,468 ****
--- 476,495 ----
  void libwrap_init(int);
  void auth_libwrap(CLI *);
  
+ /**************************************** Prototypes for ident.c */
+ #define IDENT_ADMIN_ALLOWED_DEFAULT "localhost"
+ #define IDENT_FIELDS_DEFAULT         1
+ #define IDENT_PORT_DEFAULT         113
+ #define IDENT_ADMIN_PORT_DEFAULT   790
+ #define PORT_MAX                 65535
+ 
+ int create_ident_socket();
+ int create_ident_admin_socket();
+ void process_ident_request(int fd);
+ void process_ident_admin_request(int fd);
+ void add_ident_connection(CLI *c);
+ void remove_ident_connection(CLI *c);
+ 
  #endif /* defined PROTOTYPES_H */
  
  /* End of prototypes.h */
*** stunnel-4.26/src/options.c	2008-06-21 23:18:23.000000000 +0200
--- stunnel-4.26-identprop/src/options.c	2008-12-18 10:23:57.000000000 +0100
***************
*** 264,269 ****
--- 264,330 ----
      }
  #endif
  
+     /* IdentAdmin */
+     switch(cmd) {
+     case CMD_INIT:
+         memset(&options.ident_admin, 0, sizeof(SOCKADDR_LIST));
+         options.ident_admin.addr[0].in.sin_port=0;
+         break;
+     case CMD_EXEC:
+         if(strcasecmp(opt, "identAdmin"))
+             break;
+         if(!name2addrlist(&options.ident_admin, arg, DEFAULT_LOOPBACK))
+             return "Failed to resolve address";
+         return NULL; /* OK */
+     case CMD_DEFAULT:
+         s_log(LOG_RAW, "%-15s = %s:%u", "identAdmin", DEFAULT_LOOPBACK, IDENT_ADMIN_PORT_DEFAULT);
+         break;
+     case CMD_HELP:
+         s_log(LOG_RAW, "%-15s = listening address for administrative ident server ", "identAdmin");
+         break;
+     }
+ 
+     /* IdentAdminAllowed */
+     switch(cmd) {
+     case CMD_INIT:
+ 		options.ident_admin_allowed=strdup(DEFAULT_LOOPBACK);
+         break;
+     case CMD_EXEC:
+         if(strcasecmp(opt, "identAdminAllowed"))
+             break;
+         if (options.ident_admin_allowed)
+         	free(options.ident_admin_allowed);
+         options.ident_admin_allowed=strdup(arg);
+         return NULL; /* OK */
+     case CMD_DEFAULT:
+         s_log(LOG_RAW, "%-15s = %s", "identAdminAllowed", IDENT_ADMIN_ALLOWED_DEFAULT);
+         break;
+     case CMD_HELP:
+         s_log(LOG_RAW, "%-15s = machine which is allowed to ask administrative ident requests ", "identAdminAllowed");
+         break;
+     }
+ 
+     /* IdentBind */
+     switch(cmd) {
+     case CMD_INIT:
+         memset(&options.ident_bind, 0, sizeof(SOCKADDR_LIST));
+         options.ident_bind.addr[0].in.sin_family=AF_INET;
+         options.ident_bind.addr[0].in.sin_port=htons(IDENT_PORT_DEFAULT);
+         break;
+     case CMD_EXEC:
+         if(strcasecmp(opt, "identBind"))
+             break;
+         if(!name2addrlist(&options.ident_bind, arg, DEFAULT_ANY))
+             return "Failed to resolve address";
+         return NULL; /* OK */
+     case CMD_DEFAULT:
+         s_log(LOG_RAW, "%-15s = %s:%u", "identBind", DEFAULT_ANY, IDENT_PORT_DEFAULT);
+         break;
+     case CMD_HELP:
+         s_log(LOG_RAW, "%-15s = listening address for ident server ", "identBind");
+         break;
+     }
+ 
      /* output */
      switch(cmd) {
      case CMD_INIT:
***************
*** 697,702 ****
--- 758,810 ----
          break;
      }
  
+     /* IdentServer */
+     switch(cmd) {
+     case CMD_INIT:
+         section->option.ident=0; 
+         section->connection_list=NULL;
+         break;
+     case CMD_EXEC:
+         if(strcasecmp(opt, "identServer"))
+             break;
+         if(!strcasecmp(arg, "yes")) {
+             section->option.ident=1;
+             options.option.ident_server=1;
+         }
+         else if(!strcasecmp(arg, "no"))
+             section->option.ident=0;
+         else
+             return "Argument should be either 'yes' or 'no'";
+         return NULL; /* OK */
+     case CMD_DEFAULT:
+         break;
+     case CMD_HELP:
+         s_log(LOG_RAW, "%-15s = yes|no activate ident server", "identServer");
+         break;
+     }
+ 
+     /* IdentFields */
+     switch(cmd) {
+     case CMD_INIT:
+         section->ident_fields=IDENT_FIELDS_DEFAULT;
+         break;
+     case CMD_EXEC:
+         if(strcasecmp(opt, "identFields"))
+             break;
+         if (section->option.ident==0)
+         	return "Ident must be set to yes prior to set IdentFields";
+         section->ident_fields=atoi(arg);
+         if((section->ident_fields < 1)||(section->ident_fields > 1023)) 
+         	return "IdentFields must be set between 1 and 1023 (see manual)";
+         return NULL; /* OK */
+     case CMD_DEFAULT:
+         s_log(LOG_RAW, "%-15s = %u", "identFields", IDENT_FIELDS_DEFAULT);
+         break;
+     case CMD_HELP:
+         s_log(LOG_RAW, "%-15s = 1 to 1023 (see manual)", "identFields");
+         break;
+     }
+ 
      /* CRLpath */
      switch(cmd) {
      case CMD_INIT:
*** stunnel-4.26/src/client.c	2008-03-27 09:35:27.000000000 +0100
--- stunnel-4.26-identprop/src/client.c	2008-12-18 10:23:57.000000000 +0100
***************
*** 154,159 ****
--- 154,160 ----
           error==1 ? "reset" : "closed", c->ssl_bytes, c->sock_bytes);
  
          /* Cleanup IDENT socket */
+     remove_ident_connection(c);
      if(c->fd>=0)
          closesocket(c->fd);
  
***************
*** 261,266 ****
--- 262,268 ----
      /* setup c->remote_fd, now */
      if(c->opt->option.remote) {
          c->remote_fd.fd=connect_remote(c);
+         add_ident_connection(c);
      } else /* NOT in remote mode */
          c->remote_fd.fd=connect_local(c);
      c->remote_fd.is_socket=1; /* Always! */
*** stunnel-4.26/src/ident.c	2009-01-09 10:46:55.000000000 +0100
--- stunnel-4.26-identprop/src/ident.c	2009-01-05 18:12:01.000000000 +0100
***************
*** 0 ****
--- 1,668 ----
+ /*
+  *   ident.c   patch added for Universal SSL tunnel
+  *   Copyright (C) 2008 Christophe Nanteuil <christophe.nanteuil@gmail.com>
+  *
+  *   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, see <http://www.gnu.org/licenses>.
+  * 
+  *   The socket dialogs, using the ident protocol specified in RFC 1413,
+  *   with programs that stunnel connects to in order to provide identification 
+  *   propagation from stunnel clients certificates.
+  */
+ 
+ #include "common.h"
+ #include "prototypes.h"
+ 
+ #include <sys/types.h> 
+ #include <sys/socket.h>
+ #include <assert.h>
+ 
+ /* error messages RFC 1413 compliant */
+ #define MSG_ERR_UNKNOWN "ERROR : UNKNOWN-ERROR"
+ #define MSG_ERR_NOUSER  "ERROR : NO-USER"
+ 
+ /* selection of fields to print to ident requests */
+ typedef int NameFields;
+ #define FIELD_SEP                 '/'
+ #define COMMON_NAME_S               1
+ #define ORG_UNIT_S                  2
+ #define ORG_S                       4
+ #define LOCALITY_S                  8
+ #define COUNTRY_S                  16
+ #define COMMON_NAME_I              32
+ #define ORG_UNIT_I                 64
+ #define ORG_I                     128
+ #define LOCALITY_I                256
+ #define COUNTRY_I                 512
+ #define SENTINEL_NAMEFIELD       1024
+ 
+ #define NB_FIELDS                   5
+ #define EX_MEM 2
+ 
+ /**
+  * A chained list seems fast enough to process all users
+  **/
+ struct _user_list {
+ 	struct _user_list *next;
+ 	struct _user_list *prev;
+ 	char *dn;
+ 	char *idn;
+ 	unsigned int nb_conns;
+ };
+ 
+ typedef struct _user_list Userlist;
+ 
+ static Userlist head_user = { NULL, NULL, NULL, 0};
+ static unsigned int nb_users = 0;
+ static unsigned int max_users = 0;
+ static unsigned int nb_connections = 0;
+ static unsigned int max_connections = 0;
+ static unsigned int max_connections_user = 0;
+ 
+ static int get_local_port(CLI *c) {
+     SOCKADDR_UNION loc_addr;
+     socklen_t addrlen=sizeof(SOCKADDR_UNION);
+     assert(c!=NULL);
+     memset(&loc_addr, 0, addrlen);
+     if(getsockname(c->remote_fd.fd, (struct sockaddr *)&loc_addr, 
+             &addrlen)) {
+         sockerror("getsockname");
+         return 0;
+     } else 
+         return ntohs(loc_addr.in.sin_port);
+ }
+ 
+ /*****************************
+  * Userlist related functions
+  **/
+ 
+ /**
+  * finds user in list 
+  * @param head start of the users list 
+  * @param dn distinguished name of user
+  * @param idn distinguished name of issuer 
+  * @return pointer to the user or NULL if user does not exists in list
+  **/
+ static Userlist *find_user_in_list(const Userlist *head, 
+ 	const char *dn, const char *idn) {
+ 	Userlist *usr = head->next;
+ 	
+ 	assert((dn!=NULL)&&(idn!=NULL));
+ 	
+ 	while ((usr!=NULL) && 
+ 	  (usr->dn!=NULL) && (strcmp( usr->dn, dn)!=0) &&
+ 	  (usr->idn!=NULL) && (strcmp( usr->idn, idn)!=0))
+ 		usr = usr->next;
+ 	return usr;
+ } /* find_user_in_list */
+ 
+ /**
+  * adds one connection to user account in user list
+  * if the user has no previous connection, creates an entry in the list
+  * We are in critical section (called from add_connection)
+  * @param head start of the users list 
+  * @param dn distinguished name of user
+  * @param idn distinguished name of issuer 
+  * @return 0 if ok
+  *        <0 if error
+  **/
+ static int add_conn_user(Userlist *head, const char *dn, const char *idn) {
+ 	Userlist *usr;
+ 	
+ 	assert((dn!=NULL)&&(idn!=NULL));
+ 	
+ 	if ((usr=find_user_in_list( head, dn, idn))!=NULL) {
+ 		if (++usr->nb_conns>max_connections_user)
+ 			max_connections_user=usr->nb_conns;
+ 		return 0;
+ 	} 
+ 	if (((usr=malloc(sizeof(Userlist)))==NULL) ||
+ 		((usr->dn=strdup(dn))==NULL) ||
+ 		((usr->idn=strdup(idn))==NULL)) {
+ 		s_log( LOG_ERR, "Memory Allocation failed");
+ 		return -1;
+ 	}
+ 	usr->nb_conns = 1;
+ 	usr->next = head->next;
+ 	usr->prev = head;
+ 	if (usr->next != NULL)
+ 		usr->next->prev = usr;
+ 	head->next = usr;
+ 	if (++nb_users>max_users)
+ 		max_users=nb_users;
+ 	return 0;
+ } /* add_conn_user */
+ 
+ /**
+  * removes one connection to user account in user list
+  * if the user has no more connection, deletes the entry from list
+  * We are in critical section (called from remove_connection)
+  * @param head start of the users list 
+  * @param dn distinguished name of user
+  * @param idn distinguished name of issuer 
+  * @return 0 if ok
+  *        <0 if error
+  **/
+ static int rmv_conn_user(Userlist *head, const char *dn, const char *idn) {
+ 	Userlist *usr;		
+ 
+ 	assert((dn!=NULL)&&(idn!=NULL));
+ 	
+ 	if ((usr=find_user_in_list( head, dn, idn))==NULL) {
+ 		s_log( LOG_ERR, "User %s(%s) not found in user list", dn,idn);
+ 		return -1;
+ 	} 
+ 	if (--usr->nb_conns>0) 
+ 		return 0;
+ 	if (usr->prev==NULL) {
+ 		s_log( LOG_ERR, "Error while processing user list");
+ 		return -1;
+ 	}
+ 	usr->prev->next=usr->next;
+ 	if (usr->next!=NULL)
+ 		usr->next->prev=usr->prev;
+ 	free(usr->dn);
+ 	free(usr->idn);
+ 	free(usr);
+ 	nb_users--;
+ 	return 0;
+ } /* rmv_conn_user */
+ 
+ /**
+  * elementary adds one field from subject to res
+  * @param fields  set describing which fields we want
+  * @param which_one the one we are processing
+  * @param subject string containing all NB_FIELDS fields 
+  *                (either subject or issuer)
+  * @param res string containing result
+  * @param lgth max length of res string
+  **/
+ static void add_field(NameFields fields, NameFields which_one, 
+ 	char *subject, char *res, unsigned int lgth) {
+     static char *heads[NB_FIELDS] = { "CN", "OU", "O", "L", "C" };
+     char head[5];
+     int i=0,j=0;
+     char *strfield;
+     
+     assert(res!=NULL);
+     assert(subject!=NULL);
+     
+     while ((which_one>>j)>1)
+         j++;
+     if (j>=NB_FIELDS)
+         j=j%NB_FIELDS;
+ #ifdef HAVE_SNPRINTF
+     snprintf(&head[0], 5,
+ #else
+     sprintf(&head[0],
+ #endif
+     "%s=", heads[j]); 
+     if ((fields & which_one) && (strfield=strstr(subject, head))) {
+         while ((res[i]!='\0') && (i<lgth-1))
+             i++;
+         if (fields==which_one) /* it is the only field */
+             strfield=&strfield[strlen(head)];
+         else
+             res[i++]=FIELD_SEP;
+         j=0;
+         while ((i<lgth)&&(strfield[j]!=FIELD_SEP)&&(strfield[j]!='\0'))
+             res[i++]=strfield[j++];   
+     }
+ } /* add_field */
+ 
+ /**
+  * get the specified fields of the user from 'oneline' subject name
+  * to construct an answer in specified format
+  * @param res result of the extraction
+  * @param lgth maximum length of res string
+  * @param content string containing 'oneline' subject name
+  * @param field which field to extract from content
+  **/
+ static void construct_name(char *res, unsigned int lgth, const char *id,
+ 	const char *idn, NameFields fields) {    
+     char *subject_name, *issuer_name, *name;
+     unsigned int i;
+     
+     assert(res!=NULL);
+     assert(lgth>0);
+     assert((id!=NULL) && (idn!=NULL));
+     
+     if (((name = malloc(lgth)) == NULL) || 
+         ((subject_name = strdup(id)) == NULL) ||
+         ((issuer_name = strdup(idn)) == NULL)) {
+         s_log( LOG_ERR, "Memory Allocation failed");
+         strncpy( res, MSG_ERR_UNKNOWN, lgth);
+         free(name);
+         free(subject_name);
+         free(issuer_name);
+         return;
+     }
+     memset(name, 0, lgth);
+     
+     for (i = COMMON_NAME_S ; i < COMMON_NAME_I ; i <<= 1)
+         add_field( fields, i, subject_name, name, lgth);
+     if (issuer_name != NULL)
+         for (i = COMMON_NAME_I ; i < SENTINEL_NAMEFIELD ; i <<=1)
+             add_field( fields, i, issuer_name, name, lgth); 
+     if (name[0] == '\0')
+         strncpy( res, MSG_ERR_UNKNOWN, lgth);
+     else
+ #ifdef HAVE_SNPRINTF
+     snprintf(res, lgth,
+ #else
+     sprintf(res,
+ #endif
+         " USERID : OTHER : %s", name);
+ 	free(name);
+ 	free(subject_name);
+ 	free(issuer_name);
+ } /* construct_name */
+ 
+ /*************************************
+  * global connection related functions
+  **/
+ 
+ void add_ident_connection(CLI *c) {
+ 	char dn[STRLEN],idn[STRLEN];
+ 	X509 *peer_cert;
+ 	
+ 	assert(c!=NULL);
+ 	
+ 	if ((peer_cert = SSL_get_peer_certificate(c->ssl)) != NULL) {
+ 		X509_NAME_oneline(X509_get_subject_name(peer_cert), dn, STRLEN);
+ 		X509_NAME_oneline(X509_get_issuer_name(peer_cert), idn, STRLEN);
+ 		X509_free(peer_cert);
+ 		if (add_conn_user( &head_user, dn, idn)<0)
+ 			s_log( LOG_ERR, "Error while adding connection to user list."); 
+ 	} 
+ 	enter_critical_section(CRIT_IDENT); 
+ 	c->next=c->opt->connection_list;
+ 	c->opt->connection_list=c;
+ 	c->local_port=get_local_port(c);
+ 	leave_critical_section(CRIT_IDENT); 
+ 	if (++nb_connections>max_connections)
+ 		max_connections=nb_connections;
+ } /* add_ident_connection */
+ 
+ void remove_ident_connection(CLI *c) {
+ 	CLI *current, *prev;
+ 	char dn[STRLEN],idn[STRLEN];
+ 	X509 *peer_cert;
+ 
+ 	assert(c!=NULL);
+ 	
+ 	if ((peer_cert = SSL_get_peer_certificate(c->ssl)) != NULL) {
+ 		X509_NAME_oneline(X509_get_subject_name(peer_cert), dn, STRLEN);
+ 		X509_NAME_oneline(X509_get_issuer_name(peer_cert), idn, STRLEN);
+ 		X509_free(peer_cert);
+ 	} 
+ 	enter_critical_section(CRIT_IDENT);	
+ 	rmv_conn_user( &head_user, dn, idn);
+ 	prev=current=c->opt->connection_list;
+ 	if (current==NULL) {
+ 		leave_critical_section(CRIT_IDENT); 
+ 		s_log( LOG_ERR, "Error : No connection in list, cannot remove");
+ 		return;
+ 	}
+ 	while ((current!=NULL)&&(current!=c)) {
+ 		prev=current;
+ 		current=current->next;
+ 	}
+ 	if (current==NULL) {
+ 		leave_critical_section(CRIT_IDENT); 
+ 		s_log( LOG_ERR, "Error while removing connection (FD:%i)",
+ 			c->remote_fd.fd);
+ 		return;
+ 	}
+ 	if (prev==current)
+ 		c->opt->connection_list=c->next;
+ 	else
+ 		prev->next=current->next;
+ 	leave_critical_section(CRIT_IDENT); 
+ 	nb_connections--;
+ } /* remove_ident_connection */
+ 
+ /**
+  * gets dn and idn from SSL context of the connection
+  * @param port source port of connection
+  * @param section service associated to the connection
+  * @param dn string where to put subject distinguished name of remote user certificate
+  * @param idn string where to put issuer distinguished name of remote user certificate
+  * @return 0 if success , <0 if error
+  **/
+ static int get_peer_names(Port port, LOCAL_OPTIONS *section, char *dn, char *idn) {
+ 	char name[STRLEN];
+ 	X509 *peer_cert;
+ 	CLI *conn;
+ 
+ 	assert((dn!=NULL)&&(idn!=NULL));
+ 
+ 	enter_critical_section(CRIT_IDENT);
+ 	conn=section->connection_list;
+ 	while ((conn!=NULL) && (conn->local_port!=port))
+ 		conn=conn->next;
+ 	if (conn==NULL) {
+ 		s_log( LOG_ERR, "Connection from %u port not found", port);
+ 		return -1;
+ 	}
+ 	if ((peer_cert = SSL_get_peer_certificate(conn->ssl)) == NULL)
+ 		return -2;
+ 	safecopy(dn, "SSL_CLIENT_DN=");
+ 	X509_NAME_oneline(X509_get_subject_name(peer_cert), name, STRLEN);
+ 	safestring(name);
+ 	safeconcat(dn, name);
+ 	safecopy(idn, "SSL_CLIENT_I_DN=");
+ 	X509_NAME_oneline(X509_get_issuer_name(peer_cert), name, STRLEN);
+ 	X509_free(peer_cert);
+ 	leave_critical_section(CRIT_IDENT);
+ 	safestring(name);
+ 	safeconcat(idn, name);
+ 	return 0;
+ } /* get_peer_names */
+             
+ /**
+  * handles ident request from programs
+  * request must be RFC1413 compliant
+  * @param fd open socket 
+  * @return 1 if request closed
+  *         0 if request still open
+  **/
+ int handle_ident_request(int fd, struct sockaddr_in client_address,
+ 	socklen_t len) {
+     char *buffer;
+     char *dstport, *srcport;
+     Port sport, dport;
+     char name[2*STRLEN];
+     LOCAL_OPTIONS *section;
+     char dn[STRLEN], idn[STRLEN];
+     char remnumname[IPLEN], service[10];
+     int s,result=1;
+     
+     assert(fd>0);
+     assert(options.ident_admin_allowed!=NULL);
+ 
+ 	if ((buffer=malloc(BUFFSIZE))==NULL) {
+ 		s_log( LOG_ERR, "Memory Allocation Error");
+ 		goto end;
+ 	}
+     memset( buffer, '\0', BUFFSIZE);
+     
+     if (recv( fd, buffer, BUFFSIZE, 0) <= 0) {
+     	goto end;
+     }
+     
+ 	s=getnameinfo((struct sockaddr*)&client_address, len, 
+     	remnumname, IPLEN, service, STRLEN, NI_NUMERICHOST|NI_NUMERICSERV);
+     if (s!=0) 
+ 		s_log( LOG_ERR, "getnameinfo: %s\n", gai_strerror(s));
+     
+     s_log( LOG_INFO, "handling ident request from %s (FD %u)", remnumname, fd);    
+     s_log( LOG_DEBUG, "Ident Received : %s", buffer); 
+ 
+     if ((dstport = strchr( &buffer[0], ',')) == NULL) {
+     	goto end;
+     }
+     
+     if (strlen(dstport) < 2) {
+ 		strncat( buffer, MSG_ERR_UNKNOWN, BUFFSIZE-strlen(MSG_ERR_UNKNOWN)-1);
+         send( fd, buffer, strlen(buffer), 0);
+         goto end;
+     }
+ 
+     srcport = &buffer[0];
+     sport = atoi(srcport); 
+     dstport[0] = '\0';
+     dstport++;
+     dport = atoi(dstport);
+ 
+     strncpy( name, MSG_ERR_NOUSER, STRLEN);
+     /* search service associated to the request */
+ 	for (section=local_options.next;section;section=section->next) {
+ 		unsigned int a;
+ 		for (a=0;a<MAX_HOSTS;a++) {
+ 			if ((section->remote_addr.addr[a].in.sin_addr.s_addr==client_address.sin_addr.s_addr) &&
+ 				(ntohs(section->remote_addr.addr[a].in.sin_port) == dport)) {
+ 				if (!section->option.ident) {
+ 					s_log( LOG_INFO, "Ident not activated for service %s", 
+ 						section->servname);
+ 					goto found;
+ 				} 
+ 				if (get_peer_names( sport, section, dn, idn) == 0)
+ 					construct_name( name, 2*STRLEN, dn, idn, section->ident_fields);				
+ 				goto found;
+ 			} 
+ 		}
+ 	}
+ 	s_log( LOG_ERR, "%u:%u does not match any service", client_address.sin_addr.s_addr, dport);
+             
+ found:
+ #ifdef HAVE_SNPRINTF
+     snprintf( buffer, BUFFSIZE,
+ #else
+     sprintf( buffer,
+ #endif
+         "%u , %u : %s\n", sport, dport, name);
+         
+     send( fd, buffer, strlen(buffer), 0);
+     result=0;
+     buffer[strlen(buffer)-1]='\0';
+     s_log( LOG_DEBUG, "Ident Sent : %s", buffer); 
+     s_log( LOG_INFO, "Ident request finished (FD %u)", fd);
+ end:
+ 	if (result==1) {
+         shutdown(fd, SHUT_RDWR);
+         close(fd);
+     }
+     free(buffer);
+     return result;
+ } /* handle_ident_request */
+ 
+ /**
+  * Process ident admin request
+  * request can be : 
+  * 		- list_users : list users currently connected and number of connections for each user
+  * 		- max_users : maximum number of simultaneous users since start
+  *		- max_conns : maximum number of simultaneous connections since start
+  * 		- nb_users : current number of users connected
+  * 		- nb_conns : current number of simultaneous connections
+  * @param fd open socket 
+  **/
+ void process_ident_admin_request(int fd) {
+ 	struct sockaddr_in client_address;
+     char *buffer;
+     char remhostname[IPLEN], remnumname[IPLEN], service[10];
+     int res,fdi;
+     socklen_t len;
+ 
+     assert(fd>0);
+ 
+ 	memset(&client_address, 0, len = sizeof(client_address));
+     if ((fdi = accept( fd, (struct sockaddr *) &client_address, &len)) < 0) {
+     	perror("accept()");
+     	return;
+     }
+     getnameinfo((struct sockaddr*)&client_address, len, 
+    		remhostname, IPLEN, service, STRLEN, NI_NUMERICSERV);
+ 	res=getnameinfo((struct sockaddr*)&client_address, len, 
+     	remnumname, IPLEN, service, STRLEN, NI_NUMERICHOST|NI_NUMERICSERV);
+     if (res!=0) {
+ 		s_log( LOG_ERR, "getnameinfo: %s\n", gai_strerror(res));
+ 		return;
+ 	}
+ 		
+    	if ((strcasecmp( remhostname, options.ident_admin_allowed)) &&
+    		(strcasecmp( remnumname, options.ident_admin_allowed))) {
+    		s_log( LOG_ERR, "%s is not allowed to process administrative Ident request.", 
+    			remnumname);
+    		return;
+    	}
+ 		
+ 	if ((buffer=malloc(BUFFSIZE))==NULL) {
+ 		s_log( LOG_ERR, "Memory Allocation Error");
+ 		goto end;
+ 	}
+     memset( buffer, '\0', BUFFSIZE);
+     
+     if (recv( fdi, buffer, BUFFSIZE, 0) <= 0) {
+         goto end;
+     }    
+     s_log( LOG_INFO, "handling ident admin request from %s (FD %u)", remnumname, fdi);    
+     s_log( LOG_DEBUG, "Admin ident Received : %s", buffer); 
+     if (!strncasecmp( &buffer[0], "CLEAR_MAX", strlen("CLEAR_MAX"))) {
+     	max_connections_user=0;
+     	max_users=0;
+     	max_connections=0;
+ #ifdef HAVE_SNPRINTF
+ 	    snprintf( buffer, BUFFSIZE,
+ #else
+ 	    sprintf( buffer,
+ #endif
+ 			"Clearing done");
+         send( fdi, buffer, strlen(buffer), 0);
+     	goto end;
+     }    
+     
+     if (!strncasecmp( &buffer[0], "NB_CONNS", strlen("NB_CONNS"))) {
+ #ifdef HAVE_SNPRINTF
+ 	    snprintf( buffer, BUFFSIZE,
+ #else
+ 	    sprintf( buffer,
+ #endif
+     	    "Nb connections : %u\n", nb_connections);
+         send( fdi, buffer, strlen(buffer), 0);
+         goto end;
+     }
+ 
+    	if (!strncasecmp( &buffer[0], "MAX_CONNS_USER", strlen("MAX_CONNS_USER"))) {	
+ #ifdef HAVE_SNPRINTF
+ 	    snprintf( buffer, BUFFSIZE,
+ #else
+ 	    sprintf( buffer,
+ #endif
+ 			"Max conns for 1 user : %u\n",max_connections_user);
+ 		send( fdi, buffer, strlen(buffer), 0);
+ 		goto end;
+ 	}
+ 
+    	if (!strncasecmp( &buffer[0], "MAX_CONNS", strlen("MAX_CONNS"))) {	
+ #ifdef HAVE_SNPRINTF
+ 	    snprintf( buffer, BUFFSIZE,
+ #else
+ 	    sprintf( buffer,
+ #endif
+ 			"Max conns : %u\n", max_connections);
+ 		send( fdi, buffer, strlen(buffer), 0);
+ 		goto end;
+ 	}
+ 
+    	if (!strncasecmp( &buffer[0], "MAX_USERS", strlen("MAX_USERS"))) {	
+ #ifdef HAVE_SNPRINTF
+ 	    snprintf( buffer, BUFFSIZE,
+ #else
+ 	    sprintf( buffer,
+ #endif
+ 			"Max users : %u\n", max_users);
+ 		send( fdi, buffer, strlen(buffer), 0);
+ 		goto end;
+ 	}
+ 
+    	if (!strncasecmp( &buffer[0], "NB_USERS", strlen("NB_USERS"))) {	
+ #ifdef HAVE_SNPRINTF
+ 	    snprintf( buffer, BUFFSIZE,
+ #else
+ 	    sprintf( buffer,
+ #endif
+ 			"Nb users : %u\n", nb_users);
+ 		send( fdi, buffer, strlen(buffer), 0);
+ 		goto end;
+ 	}
+ 	
+    	if (!strncasecmp( &buffer[0], "LIST_USERS", strlen("LIST_USERS"))) {	
+ 		Userlist *user=head_user.next;		
+ 
+ 		while (user!=NULL) {
+ 			if (user->dn!=NULL) 
+ 				snprintf( buffer, BUFFSIZE, "User : %s(%s) - %u connections\n",
+ 					user->dn, user->idn, user->nb_conns);
+ 			else
+ 				strncpy( buffer ,"User : Unknown", BUFFSIZE);
+ 			send( fdi, buffer, strlen(buffer), 0);
+ 			user=user->next;
+ 		}
+ 		goto end;
+ 	}
+ 	strncpy( buffer, "Unknown command", BUFFSIZE);
+ 	send( fdi, buffer, strlen(buffer), 0);
+ end:
+     shutdown(fdi, SHUT_RDWR);
+     close(fdi);
+     free(buffer);
+ 	return;
+ } /* handle_ident_admin_request */
+ 
+ void process_ident_request(int fdi) {
+     struct sockaddr_in client_address;
+     int res;
+     socklen_t l;
+ 
+ 	memset(&client_address, 0, l = sizeof(client_address));
+     if ((res = accept( fdi, (struct sockaddr *) &client_address, &l)) < 0) {
+     	perror("accept()");
+     } else {
+     	if (handle_ident_request( res, client_address, l) == 0) {
+     		/* close the socket as we don't know how to get it back */
+ 	        shutdown( res, SHUT_RDWR);
+ 	        close(res);
+ 	    }
+ 	}
+ } /* process_ident_socket */
+ 
+ static int create_server_socket(const char *serv_name, 
+ 	const SOCKADDR_LIST *sockaddr) {
+     SOCKADDR_UNION addr;
+     int fdi;
+     char straddr[STRLEN];
+     
+     memset(&addr, 0, sizeof(SOCKADDR_UNION));
+ 	memcpy(&addr, &sockaddr->addr[0], sizeof(SOCKADDR_UNION));
+ 	if((fdi=socket(addr.sa.sa_family, SOCK_STREAM, 0))<0) {
+ 		sockerror("local socket");
+ 		die(1);
+ 	}
+ 	if(alloc_fd(fdi))
+ 		die(1);
+ 	if(set_socket_options(fdi,1)<0)
+ 		die(1);
+ 	s_ntop( &straddr[0], &addr);
+ 	if(bind(fdi, &addr.sa, addr_len(addr))) {
+ 		s_log(LOG_ERR, "Error binding %s to %s", serv_name, &straddr[0]);
+ 		sockerror("bind");
+ 		die(1);
+ 	}
+ 	s_log(LOG_DEBUG, "%s bound to %s", serv_name, &straddr[0]);
+ 	if(listen(fdi, 5)) {
+ 		sockerror("listen");
+ 		die(1);
+ 	}
+     return fdi;
+ } /* create_server_socket */
+ 
+ int create_ident_admin_socket() {
+ 	int fd=create_server_socket( "Administrative ident server", &options.ident_admin);
+ 	s_log(LOG_DEBUG, "%s is allowed to process admnistrative ident requests", options.ident_admin_allowed);
+ 	return fd;
+ } /* create_ident_admin_socket */
+ 
+ int create_ident_socket() {
+ 	return create_server_socket( "Ident server", &options.ident_bind);
+ } /* create_ident_socket */
*** stunnel-4.26/doc/stunnel.pod	2008-03-27 11:14:08.000000000 +0100
--- stunnel-4.26-identprop/doc/stunnel.pod	2008-12-18 10:23:57.000000000 +0100
***************
*** 175,180 ****
--- 175,233 ----
  
  default: background in daemon mode
  
+ =item B<identAdmin> = [host:]port
+ 
+ Accept administrative ident connections on the specified host:port.
+ 
+ If no host specified, defaults to all IP addresses for the local host.
+ 
+ The available requests are :
+ 
+ =over 2
+ 
+ =item *
+ 
+ max_conns : maximum number of simultaneous connections since start.
+ 
+ =item *
+ 
+ max_users : maximum number of simultaneous users since start.
+ 
+ =item *
+ 
+ max_conns_user : maximum number of simultaneous connections for one user since start.
+ 
+ =item *
+ 
+ nb_conns : current number of simultaneous connections.
+ 
+ =item *
+ 
+ nb_users : current number of users connected.
+ 
+ =item *
+ 
+ list_users : list users currently connected and number of connections
+ for each user.
+ 
+ =back
+ 
+ default: 127.0.0.1:790 (loopback interface on localhost, port 790)
+ 
+ =item B<identAdminAllowed> = host
+ 
+ The specified host is allowed to ask adminitrative ident requests (see identAdmin)
+ 
+ default: localhost
+ 
+ =item B<identBind> = [host:]port
+ 
+ Accept ident requests (see RFC 1413) on the specified host.
+ 
+ If no host specified, defaults to all IP addresses for the local host.
+ 
+ default: 0.0.0.0:113 (all interfaces on localhost)
+ 
  =item B<output> = file
  
  append log messages to a file instead of using syslog
***************
*** 381,386 ****
--- 434,458 ----
  
  use IDENT (RFC 1413) username checking
  
+ =item B<identFields> = number
+ 
+ Selects which combination of ident fields to answer to ident requests :
+ 
+          1 : subject Common Name (default)
+          2 : subject Organization Unit
+          4 : subject Organization
+          8 : subject Locality
+         16 : subject Country
+         32 : issuer Common Name
+         64 : issuer Organization Unit
+        128 : issuer Organization
+        256 : issuer Locality
+        512 : issuer Country
+ 
+ =item B<identServer> = yes|no
+ 
+ Activate ident server for this service.
+ 
  =item B<key> = keyfile
  
  private key for certificate specified with I<cert> option
*** stunnel-4.26/doc/stunnel.fr.pod	2007-09-23 17:29:19.000000000 +0200
--- stunnel-4.26-identprop/doc/stunnel.fr.pod	2008-12-18 10:23:57.000000000 +0100
***************
*** 197,202 ****
--- 197,262 ----
  
  Par d�faultE<nbsp>: arri�re-plan en mode daemon.
  
+ =item B<identAdmin> = [h�te]:port
+ 
+ Accepte des connexions d'administration pour l'identification sur cette 
+ adresse et ce port. 
+ 
+ Si l'h�te n'est pas indiqu�, le port est ouvert pour toutes les adresses IP de
+ la machine locale.
+ 
+ Les requ�tes possibles sont :
+ 
+ =over 2
+ 
+ =item *
+ 
+ max_conns : nombre maximum de connexions simultan�es depuis le lancement 
+ de stunnel.
+ 
+ =item *
+ 
+ max_users : nombre maximum d'utilisateurs simultan�s depuis le lancement 
+ de stunnel.
+ 
+ =item *
+ 
+ max_conns_user : nombre maximum de connexions simultan�es pour un 
+ utilisateur depuis le lancement de stunnel.
+ 
+ =item *
+ 
+ nb_conns : nombre actuel de connexions simultan�es.
+ 
+ =item *
+ 
+ nb_users : nombre actuel d'utilisateurs connect�s.
+ 
+ =item *
+ 
+ list_users : liste des utilisateurs actuellement connect�s et nombre de 
+ connexions pour chacun des utilisateurs.
+ 
+ =back
+ 
+ Par d�fautE<nbsp>: 127.0.0.1:790 (interface locale sur le port 790)
+ 
+ =item B<identAdminAllowed> = h�te
+ 
+ La machine sp�cifi�e est autoris�e � effectuer des requ�tes administratives
+ au serveur administratif d'identification (voir identAdmin)
+ 
+ Par d�fautE<nbsp>: localhost
+ 
+ =item B<identBind> = [h�te]:port
+ 
+ Accepte des connexions d'identification (RFC 1413) sur cette adresse. 
+ 
+ Si l'h�te n'est pas indiqu�, le port est ouvert pour toutes les adresses IP de
+ la machine locale.
+ 
+ Par d�fautE<nbsp>: 0.0.0.0:113 (toutes les interfaces sur le port 113)
+ 
  =item B<key> = fichier
  
  Fichier de clef priv�e pour le certificat sp�cifi� par I<cert>
***************
*** 370,375 ****
--- 430,454 ----
  
  Applique le contr�le d'identit� d'utilisateur IDENT (RFC 1413)
  
+ =item B<identFields> = nombre
+ 
+ s�lection des champs d'identification � retourner lors des requ�tes d'identification
+ 
+          1 : subject Common Name (par d�faut)
+          2 : subject Organization Unit
+          4 : subject Organization
+          8 : subject Locality
+         16 : subject Country
+         32 : issuer Common Name
+         64 : issuer Organization Unit
+        128 : issuer Organization
+        256 : issuer Locality
+        512 : issuer Country
+ 
+ =item B<identServer> = yes|no
+ 
+ Active ou d�sactive le serveur d'identification pour ce service.
+ 
  =item B<local> = h�te
  
  Adresse IP de l'interface de sortie utilis�e pour les connexions distantes.
