--- 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,7 +3,7 @@ # 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 +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 2009-02-02 13:37:12.000000000 +0100 @@ -130,6 +130,7 @@ 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,6 +172,14 @@ #endif s_poll_add(&fds, opt->fd, 1, 0); } + + if (options.option.ident_server && (fd_ident==0)) { + if ((fd_ident=create_ident_socket())>0) { + s_poll_add( &fds, fd_ident, 1, 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)) @@ -200,6 +209,10 @@ if(s_poll_canread(&fds, opt->fd)) accept_connection(opt); + if((fd_ident!=0)&&(s_poll_canread(&fds, fd_ident))) + accept_ident_request(fd_ident); + if((fd_ident_admin!=0)&&(s_poll_canread(&fds, fd_ident_admin))) + accept_iadmin_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 2009-02-02 13:35:39.000000000 +0100 @@ -145,6 +145,11 @@ char *win32_service; #endif + /* Ident Server data 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 @@ -161,6 +166,8 @@ unsigned int foreground:1; unsigned int syslog:1; #endif + unsigned int ident_server:1; /* set if at least one service ask for */ + unsigned int ident_mask_error:1; #ifdef USE_FIPS unsigned int fips:1; /* enable FIPS 140-2 mode */ #endif @@ -182,6 +189,7 @@ #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 */ @@ -209,6 +217,7 @@ 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,6 +238,7 @@ 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,8 +322,13 @@ int is_socket; /* File descriptor is a socket */ } FD; -typedef struct { +typedef unsigned int Port; + +typedef struct _CLI { LOCAL_OPTIONS *opt; + jmp_buf err; /* moved here to be compliant with IdentRequestParams */ + 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 */ @@ -323,7 +338,6 @@ /* IP for explicit local bind or transparent proxy */ unsigned long pid; /* PID of local process */ int fd; /* Temporary file descriptor */ - jmp_buf err; char sock_buff[BUFFSIZE]; /* Socket read buffer */ char ssl_buff[BUFFSIZE]; /* SSL read buffer */ @@ -377,7 +391,7 @@ typedef enum { CRIT_KEYGEN, CRIT_INET, CRIT_CLIENTS, CRIT_WIN_LOG, CRIT_SESSION, - CRIT_LIBWRAP, CRIT_SSL, CRIT_SECTIONS + CRIT_IDENT, CRIT_LIBWRAP, CRIT_SSL, CRIT_SECTIONS } SECTION_CODE; void enter_critical_section(SECTION_CODE); @@ -463,6 +477,33 @@ 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 accept_ident_request(int fd_ident); +void accept_iadmin_request(int fd_admin); +void add_ident_connection(CLI *c); +void remove_ident_connection(CLI *c); + +struct _IdentRequestParams { + /* opt and err must be first fields to be compliant with CLI structure */ + LOCAL_OPTIONS *opt; /* needed for stack_size and timeout_busy */ + jmp_buf err; /* needed by some functions to exit from errors */ + int fd; + int sock_bytes; /* counter of sent bytes on the socket fd */ + struct sockaddr_in client_addr; + socklen_t len; + char remnumname[IPLEN]; +}; + +typedef struct _IdentRequestParams IdentRequestParams; + #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 2009-01-26 17:39:23.000000000 +0100 @@ -264,6 +264,89 @@ } #endif + /* IdentAdmin */ + switch(cmd) { + case CMD_INIT: + memset(&options.ident_admin, 0, sizeof(SOCKADDR_LIST)); + hostport2addrlist( &options.ident_admin, DEFAULT_LOOPBACK, IDENT_ADMIN_PORT_DEFAULT); + 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:%s", "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; + } + + /* IdentMaskError */ + switch(cmd) { + case CMD_INIT: + options.option.ident_mask_error=0; + break; + case CMD_EXEC: + if(strcasecmp(opt, "identMaskError")) + break; + if(!strcasecmp(arg, "yes")) + options.option.ident_mask_error=1; + else if(!strcasecmp(arg, "no")) + options.option.ident_mask_error=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 return UNKNOWN-ERROR in lieu of other specific error code", "identMaskError"); + break; + } + /* output */ switch(cmd) { case CMD_INIT: @@ -697,6 +780,53 @@ 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,6 +154,7 @@ 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,6 +262,7 @@ /* 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-02-02 13:51:09.000000000 +0100 +++ stunnel-4.26-identprop/src/ident.c 2009-02-02 13:49:54.000000000 +0100 @@ -0,0 +1,755 @@ +/* + * ident.c patch added for Universal SSL tunnel + * Copyright (C) 2008 Christophe Nanteuil + * + * 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 . + * + * 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 +#include +#include + +/* RFC 1413 compliant error messages */ +#define IDT_MSG_INVALIDPORT "ERROR : INVALID-PORT" +#define IDT_MSG_UNKNOWN "ERROR : UNKNOWN-ERROR" +#define IDT_MSG_NOUSER "ERROR : NO-USER" +#define IDT_MSG_HIDDENUSER "ERROR : HIDDEN-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 NB_FIELDS 5 +#define SENTINEL_NAMEFIELD_S 1<remote_fd.fd, (struct sockaddr *)&loc_addr, + &addrlen)) { + sockerror("getsockname"); + return 0; + } else + return ntohs(loc_addr.in.sin_port); +} /* get_local_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_ident_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_ident_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 remove_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--; + assert(nb_users>=0); + return 0; +} /* remove_conn_user */ + +/** + * elementary append 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))) { + /* find end of res */ + while ((res[i]!='\0') && (i0); + 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, IDT_MSG_UNKNOWN, lgth); + free(name); + free(subject_name); + free(issuer_name); + return; + } + memset(name, 0, lgth); + + for (i = COMMON_NAME_S ; i < SENTINEL_NAMEFIELD_S ; i <<= 1) + add_field( fields, i, subject_name, name, lgth); + for (i = COMMON_NAME_I ; i < SENTINEL_NAMEFIELD_I ; i <<=1) + add_field( fields, i, issuer_name, name, lgth); + if (name[0] == '\0') + strncpy( res, IDT_MSG_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 + **/ + +/** + * determine the dn and idn of the user to add the connection to + * the chained list of users + * and add the connection to the chained list of service c + * @param c connection parameters + **/ +void add_ident_connection(CLI *c) { + char dn[STRLEN],idn[STRLEN]; + X509 *peer_cert; + + assert(c!=NULL); + + dn[0]=idn[0]='\0'; + if ((peer_cert=SSL_get_peer_certificate(c->ssl))==NULL) return; + 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); + if (dn[0] && idn[0]) + if (add_conn_user( &head_user, dn, idn)<0) + s_log( LOG_ERR, "Error while adding connection to user list."); + 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 */ + +/** + * removes the connection to the chained list of service c + * and to the chained list of users + * @param c connection parameters + **/ +void remove_ident_connection(CLI *c) { + CLI *current, *prev; + char dn[STRLEN],idn[STRLEN]; + X509 *peer_cert; + + assert(c!=NULL); + + dn[0]=idn[0]='\0'; + if ((peer_cert = SSL_get_peer_certificate(c->ssl)) == NULL) return; + 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); + if (dn[0] && idn[0]) + remove_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 housing subject distinguished name of remote user certificate + * @param idn string housing 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; + leave_critical_section(CRIT_IDENT); + if (conn==NULL) { + s_log( LOG_ERR, "Connection from port %u 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); + safestring(name); + safeconcat(idn, name); + return 0; +} /* get_peer_names */ + +static void do_ident_serve(IdentRequestParams *params) { + Port sport=60, dport=8080; + int num; + char name[2*STRLEN]; + char dn[STRLEN], idn[STRLEN]; + LOCAL_OPTIONS *section; + + while (1) { + if (fdscanf((CLI *)params, params->fd, "%[^\n]", &name[0]) < 1) { + s_log( LOG_ERR, "Unable to parse request"); + longjmp(params->err, 1); + } + if (sscanf( &name[0], " %u , %u ", &sport, &dport) < 2) { + s_log( LOG_ERR, "Unable to parse buffer %s", name); + longjmp(params->err, 1); + } + if ((sport <= 0) || (dport <= 0) || + (sport > PORT_MAX) || (dport > PORT_MAX)) { + params->sock_bytes+=fdprintf((CLI *)params, params->fd, + "%u,%u: %s", sport, dport, IDT_MSG_INVALIDPORT); + longjmp(params->err, 1); + } + + /* search service associated to the request */ + for (section=local_options.next;section;section=section->next) { + unsigned int a; + for (a=0;aremote_addr.addr[a].in.sin_addr.s_addr==params->client_addr.sin_addr.s_addr) + && (ntohs(section->remote_addr.addr[a].in.sin_port) == dport)) { + if (!section->option.ident) { + if (options.option.ident_mask_error) + strncpy( name, IDT_MSG_UNKNOWN, STRLEN); + else + strncpy( name, IDT_MSG_HIDDENUSER, STRLEN); + s_log( LOG_INFO, "Ident not activated for service %s", + section->servname); + goto send; + } + if (get_peer_names( sport, section, dn, idn) == 0) { + construct_name( name, 2*STRLEN, dn, idn, + section->ident_fields); + } else { + if (options.option.ident_mask_error) + strncpy( name, IDT_MSG_UNKNOWN, STRLEN); + else + strncpy( name, IDT_MSG_NOUSER, STRLEN); + } /* if get_peer_names */ + goto send; + } /* if good host and good port */ + } /* for host in service */ + } /* for service */ + s_log( LOG_ERR, "Ident : %s:%u does not match any service", + params->remnumname, dport); + if (options.option.ident_mask_error) + strncpy( name, IDT_MSG_UNKNOWN, STRLEN); + else + strncpy( name, IDT_MSG_NOUSER, STRLEN); +send: + num=fdprintf((CLI *) params, params->fd, "%5u,%5u :%s", + sport, dport, name); + switch(num) { + case -1: /* error */ + sockerror("writesocket"); + longjmp(params->err, 1); + case 0: + s_log(LOG_DEBUG, "No data written to the socket: retrying"); + goto send; + default: + params->sock_bytes+=num; + } /* switch num */ + + } /* while 1 */ +} /* do_ident_serve */ + +static void run_ident_serve(IdentRequestParams *params) { + int error; + int s; + char service[10]; + + s=getnameinfo( (struct sockaddr*) ¶ms->client_addr, params->len, + params->remnumname, IPLEN, service, 10, NI_NUMERICHOST|NI_NUMERICSERV); + if (s!=0) { + s_log( LOG_ERR, "getnameinfo: %s\n", gai_strerror(s)); + return; + } + + s_log( LOG_INFO, "handling ident request from %s (FD %u)", + params->remnumname, params->fd); + error=setjmp(params->err); + if(!error) + do_ident_serve(params); + + s_log(LOG_NOTICE, + "Connection %s: %d bytes sent ", + error==1 ? "reset" : "closed", params->sock_bytes); + if (params->fd>0) + closesocket(params->fd); +} /* run_ident_serve */ + +/** + * handles ident FRC 1413 requests from programs that stunnel connects to + * @param params parameters of ident connection + **/ +static void *handle_ident(IdentRequestParams *params) { + assert(params); + assert(params->fd>0); + + s_log(LOG_DEBUG, "Ident started"); + if (alloc_fd(params->fd)) + return NULL; + + run_ident_serve(params); + s_log( LOG_INFO, "Ident finished"); + if (params && params->opt) free(params->opt); + if (params) free(params); +#if defined(USE_WIN32) && !defined(_WIN32_WCE) + _endthread(); +#endif +#ifdef USE_UCONTEXT + s_log(LOG_DEBUG, "Context %ld closed", ready_head->id); + s_poll_wait(NULL, 0, 0); /* wait on poll() */ + s_log(LOG_ERR, "INTERNAL ERROR: failed to drop context"); +#endif + return NULL; +} /* handle_ident */ + +/** + * Process ident admin requests. + * Connection is closed on client request (quit) or on bad command. + * request can be : + * - clear_max : reset max counters + * - nb_conns : current number of simultaneous connections + * - max_conns_user : maximum number of simultaneous connections since start + * - 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 + * - list_users : list users currently connected and number of connections for each user + * - quit : close connection with ident admin server + * @param arg parameters of ident connection + **/ +static void run_iadmin_serve(IdentRequestParams *params) { + char buffer[STRLEN]; + + while (1) { + memset( buffer, '\0', STRLEN); + fdscanf((CLI *)params, params->fd, "%s", buffer); + + if (!strcasecmp( &buffer[0], "CLEAR_MAX")) { + max_connections_user=0; + max_users=0; + max_connections=0; + params->sock_bytes+=fdprintf((CLI *)params, params->fd, + "Clearing done"); + continue; + } + + if (!strcasecmp( &buffer[0], "NB_CONNS")) { + params->sock_bytes+=fdprintf((CLI *)params, params->fd, + "Nb connections : %u", nb_connections); + continue; } + + if (!strcasecmp( &buffer[0], "MAX_CONNS_USER")) { + params->sock_bytes+=fdprintf((CLI *)params, params->fd, + "Max conns for 1 user : %u",max_connections_user); + continue; } + + if (!strcasecmp( &buffer[0], "MAX_CONNS")) { + params->sock_bytes+=fdprintf((CLI *)params, params->fd, + "Max conns : %u", max_connections); + continue; } + + if (!strcasecmp( &buffer[0], "MAX_USERS")) { + params->sock_bytes+=fdprintf((CLI *)params, params->fd, + "Max users : %u", max_users); + continue; } + + if (!strcasecmp( &buffer[0], "NB_USERS")) { + params->sock_bytes+=fdprintf((CLI *)params, params->fd, + "Nb users : %u", nb_users); + continue; } + + if (!strcasecmp( &buffer[0], "LIST_USERS")) { + Userlist *user=head_user.next; + unsigned int nblines=0; + while (user!=NULL) { + if ((user->dn) && (user->idn)) + params->sock_bytes+=fdprintf((CLI *)params, params->fd, + "User : %s(%s) - %u connections", + user->dn, user->idn, user->nb_conns); + else + params->sock_bytes+=fdprintf((CLI *)params, params->fd, + "User : Unknown"); + user=user->next; + nblines++; + } + params->sock_bytes+=fdprintf((CLI *)params, params->fd, + "End of list (%u lines)", nblines); + continue; + } + + if (!strcasecmp( &buffer[0], "QUIT")) { + params->sock_bytes+=fdprintf((CLI *)params, params->fd, + "Bye."); + break; + } + params->sock_bytes+=fdprintf((CLI *)params, params->fd, + "Unknown command"); + break; + } /* while 1 */ +} /* run_iadmin_serve */ + +static void handle_iadmin(IdentRequestParams *params) { + char remhostname[STRLEN], *tmp=NULL; + int error; + + assert(params->fd>0); + assert(options.ident_admin_allowed!=NULL); + + s_log(LOG_DEBUG, "Ident admin started"); + if ((s_ntop(remhostname, (SOCKADDR_UNION *) ¶ms->client_addr)==NULL) || + ((tmp=strstr(remhostname, ":"))==NULL)) { + closesocket(params->fd); + free(params->opt); + free(params); + return; + } + tmp[0]='\0'; /* remove port from remhostname */ + if (strcasecmp( remhostname, options.ident_admin_allowed)) { + s_log( LOG_ERR, "%s is not allowed to process administrative Ident request.", + remhostname); + closesocket(params->fd); + free(params->opt); + free(params); + return; + } + + error=setjmp(params->err); + if (!error) + run_iadmin_serve(params); + s_log(LOG_NOTICE, + "Connection %s: %d bytes sent ", + error==1 ? "reset" : "closed", params->sock_bytes); + closesocket(params->fd); + if (params && params->opt) free(params->opt); + if (params) free(params); +} /* handle_iadmin */ + +/** + * set structure params before threading/forking + * @param fd listening socket + * @return params new structure allocated on heap with following + * fields initialized : opt, fd, client_addr, sock_bytes + * left unitialized : err + **/ +static IdentRequestParams *prepare_request_handle(int fd) { + IdentRequestParams *params; + + if ((params=malloc(sizeof(IdentRequestParams)))==NULL) { + s_log( LOG_ERR, "Memory Allocation Error"); + return NULL; + } + memset( params, 0, sizeof(IdentRequestParams)); + if ((params->opt=malloc(sizeof(LOCAL_OPTIONS)))==NULL) { + s_log( LOG_ERR, "Memory Allocation Error"); + free(params); + return NULL; + } + + params->len=sizeof(params->client_addr); + while ((params->fd = accept( fd, (struct sockaddr *) ¶ms->client_addr, + ¶ms->len)) < 0) { + switch(get_last_socket_error()) { + case EINTR: + break; /* retry */ + case EMFILE: +#ifdef ENFILE + case ENFILE: +#endif +#ifdef ENOBUFS + case ENOBUFS: +#endif + case ENOMEM: + sleep(1); /* temporarily out of resources - short delay */ + default: + sockerror("accept"); + free(params->opt); + free(params); + return NULL; /* error */ + } + } + params->opt->stack_size=DEFAULT_STACK_SIZE; + params->opt->timeout_busy=30; + params->sock_bytes=0; + return params; +} /* prepare_request_handle */ + +void accept_ident_request(int fd_ident) { + IdentRequestParams *params; + + assert(fd_ident>0); + + if ((params=prepare_request_handle(fd_ident))==NULL) + return; + if (create_client( fd_ident, params->fd, (CLI *) params, (void *) handle_ident)) { + s_log(LOG_ERR, "Connection rejected: create_client failed"); + closesocket(params->fd); + free(params->opt); + free(params); + } +} /* accept_ident_request */ + +void accept_iadmin_request(int fd_admin) { + IdentRequestParams *params; + + assert(fd_admin>0); + + if ((params=prepare_request_handle(fd_admin))==NULL) + return; + if (create_client( fd_admin, params->fd, (CLI *) params, (void *) handle_iadmin)) { + s_log(LOG_ERR, "Connection rejected: create_client failed"); + closesocket(params->fd); + free(params->opt); + free(params); + } +} /* accept_iadmin_request */ + +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 2009-02-02 13:50:55.000000000 +0100 @@ -175,6 +175,67 @@ default: background in daemon mode +=item B = [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 or reset of counter. + +=item * + +max_users : maximum number of simultaneous users since start or reset of counter. + +=item * + +max_conns_user : maximum number of simultaneous connections for one user since start or reset of counter. + +=item * + +clear_max : reset max counters to zero. + +=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. + +=item * + +quit : close connection with ident admin server. + +=back + +default: 127.0.0.1:790 (loopback interface on localhost, port 790) + +=item B = host + +The specified host is allowed to ask adminitrative ident requests (see identAdmin) + +default: localhost + +=item B = [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 = file append log messages to a file instead of using syslog @@ -381,6 +442,25 @@ use IDENT (RFC 1413) username checking +=item B = 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 = yes|no + +Activate ident server for this service. + =item B = keyfile private key for certificate specified with I option --- stunnel-4.26/doc/stunnel.fr.pod 2007-09-23 17:29:19.000000000 +0200 +++ stunnel-4.26-identprop/doc/stunnel.fr.pod 2009-02-02 13:48:17.000000000 +0100 @@ -197,6 +197,75 @@ Par défaultE: arrière-plan en mode daemon. +=item B = [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 ou depuis la remise à zéro du compteur. + +=item * + +max_users : nombre maximum d'utilisateurs simultanés depuis le lancement +de stunnel ou depuis la remise à zéro du compteur. + +=item * + +max_conns_user : nombre maximum de connexions simultanées pour un +utilisateur depuis le lancement de stunnel ou depuis la remise +à zéro du compteur. + +=item * + +clear_max : remise à zéro des compteurs de maximum. + +=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. + +=item * + +quit : termine la connexion avec le serveur d'administration. + +=back + +Par défautE: 127.0.0.1:790 (interface locale sur le port 790) + +=item B = 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: localhost + +=item B = [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: 0.0.0.0:113 (toutes les interfaces sur le port 113) + =item B = fichier Fichier de clef privée pour le certificat spécifié par I @@ -370,6 +439,25 @@ Applique le contrôle d'identité d'utilisateur IDENT (RFC 1413) +=item B = 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 = yes|no + +Active ou désactive le serveur d'identification pour ce service. + =item B = hôte Adresse IP de l'interface de sortie utilisée pour les connexions distantes.