stunnel4/src/libwrap.c

322 lines
10 KiB
C
Raw Permalink Normal View History

2017-03-28 09:58:13 +02:00
/*
2017-11-15 15:03:25 +01:00
* stunnel TLS offloading and load-balancing proxy
* Copyright (C) 1998-2017 Michal Trojnara <Michal.Trojnara@stunnel.org>
2017-03-28 09:58:13 +02:00
*
* 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.
2017-03-28 10:18:03 +02:00
*
2017-03-28 09:58:13 +02:00
* 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.
2017-03-28 10:18:03 +02:00
*
2017-03-28 09:58:13 +02:00
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses>.
2017-03-28 10:18:03 +02:00
*
2017-03-28 09:58:13 +02:00
* Linking stunnel statically or dynamically with other modules is making
* a combined work based on stunnel. Thus, the terms and conditions of
* the GNU General Public License cover the whole combination.
2017-03-28 10:18:03 +02:00
*
2017-03-28 09:58:13 +02:00
* In addition, as a special exception, the copyright holder of stunnel
* gives you permission to combine stunnel with free software programs or
* libraries that are released under the GNU LGPL and with code included
* in the standard release of OpenSSL under the OpenSSL License (or
* modified versions of such code, with unchanged license). You may copy
* and distribute such a system following the terms of the GNU GPL for
* stunnel and the licenses of the other code concerned.
2017-03-28 10:18:03 +02:00
*
2017-03-28 09:58:13 +02:00
* Note that people who make modified versions of stunnel are not obligated
* to grant this special exception for their modified versions; it is their
* choice whether to do so. The GNU General Public License gives permission
* to release a modified version without this exception; this exception
* also makes it possible to release a modified version which carries
* forward this exception.
*/
#include "common.h"
#include "prototypes.h"
#ifdef USE_LIBWRAP
#include <tcpd.h>
2017-11-15 15:03:25 +01:00
#if defined(USE_PTHREAD) && !defined(__CYGWIN__)
/* http://wiki.osdev.org/Cygwin_Issues#Passing_file_descriptors */
#define USE_LIBWRAP_POOL
#endif /* USE_PTHREAD && !__CYGWIN__ */
NOEXPORT int check(char *, int);
2017-03-28 09:58:13 +02:00
int allow_severity=LOG_NOTICE, deny_severity=LOG_WARNING;
2017-11-15 15:03:25 +01:00
#ifdef USE_LIBWRAP_POOL
2017-03-28 09:58:13 +02:00
#define SERVNAME_LEN 256
2017-11-15 15:03:25 +01:00
NOEXPORT ssize_t read_fd(int, void *, size_t, int *);
NOEXPORT ssize_t write_fd(int, void *, size_t, int);
2017-03-28 09:58:13 +02:00
2017-11-15 15:03:25 +01:00
unsigned num_processes=0;
2017-03-28 09:58:13 +02:00
static int *ipc_socket, *busy;
2017-11-15 15:03:25 +01:00
#endif /* USE_LIBWRAP_POOL */
2017-03-28 09:58:13 +02:00
2017-11-15 15:03:25 +01:00
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-result"
#endif /* __GNUC__ */
2017-03-28 09:58:13 +02:00
int libwrap_init() {
2017-11-15 15:03:25 +01:00
#ifdef USE_LIBWRAP_POOL
unsigned i, j;
int rfd, result;
2017-03-28 09:58:13 +02:00
char servname[SERVNAME_LEN];
static int initialized=0;
SERVICE_OPTIONS *opt;
if(initialized) /* during startup or previous configuration file reload */
return 0;
for(opt=service_options.next; opt; opt=opt->next)
if(opt->option.libwrap) /* libwrap is enabled for this service */
break;
if(!opt) /* disabled for all sections or inetd mode (no sections) */
return 0;
num_processes=LIBWRAP_CLIENTS;
ipc_socket=str_alloc(2*num_processes*sizeof(int));
busy=str_alloc(num_processes*sizeof(int));
for(i=0; i<num_processes; ++i) { /* spawn a child */
if(s_socketpair(AF_UNIX, SOCK_STREAM, 0, ipc_socket+2*i, 0, "libwrap_init"))
return 1;
switch(fork()) {
case -1: /* error */
ioerror("fork");
return 1;
case 0: /* child */
2017-11-15 15:03:25 +01:00
tls_alloc(NULL, ui_tls, "libwrap");
2017-03-28 09:58:13 +02:00
drop_privileges(0); /* libwrap processes are not chrooted */
close(0); /* stdin */
close(1); /* stdout */
2017-11-15 15:03:25 +01:00
if(!global_options.option.log_stderr) /* for logging in read_fd */
2017-03-28 09:58:13 +02:00
close(2); /* stderr */
for(j=0; j<=i; ++j) /* close parent-side sockets created so far */
close(ipc_socket[2*j]);
while(1) { /* main libwrap child loop */
if(read_fd(ipc_socket[2*i+1], servname, SERVNAME_LEN, &rfd)<=0)
_exit(0);
result=check(servname, rfd);
2017-11-15 15:03:25 +01:00
write(ipc_socket[2*i+1], (uint8_t *)&result, sizeof result);
2017-03-28 09:58:13 +02:00
if(rfd>=0)
close(rfd);
}
default: /* parent */
close(ipc_socket[2*i+1]); /* child-side socket */
}
}
initialized=1;
2017-11-15 15:03:25 +01:00
#endif /* USE_LIBWRAP_POOL */
2017-03-28 09:58:13 +02:00
return 0;
}
2017-11-15 15:03:25 +01:00
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif /* __GNUC__ */
2017-03-28 09:58:13 +02:00
void libwrap_auth(CLI *c, char *accepted_address) {
int result=0; /* deny by default */
2017-11-15 15:03:25 +01:00
#ifdef USE_LIBWRAP_POOL
static volatile unsigned num_busy=0, roundrobin=0;
unsigned my_process;
int retval;
2017-03-28 09:58:13 +02:00
static pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond=PTHREAD_COND_INITIALIZER;
2017-11-15 15:03:25 +01:00
#endif /* USE_LIBWRAP_POOL */
2017-03-28 09:58:13 +02:00
if(!c->opt->option.libwrap) /* libwrap is disabled for this service */
return; /* allow connection */
#ifdef HAVE_STRUCT_SOCKADDR_UN
if(c->peer_addr.sa.sa_family==AF_UNIX) {
s_log(LOG_INFO, "Libwrap is not supported on Unix sockets");
return;
}
#endif
2017-11-15 15:03:25 +01:00
#ifdef USE_LIBWRAP_POOL
2017-03-28 09:58:13 +02:00
if(num_processes) {
s_log(LOG_DEBUG, "Waiting for a libwrap process");
retval=pthread_mutex_lock(&mutex);
if(retval) {
errno=retval;
ioerror("pthread_mutex_lock");
longjmp(c->err, 1);
}
while(num_busy==num_processes) { /* all child processes are busy */
retval=pthread_cond_wait(&cond, &mutex);
if(retval) {
errno=retval;
ioerror("pthread_cond_wait");
longjmp(c->err, 1);
}
}
while(busy[roundrobin]) /* find a free child process */
roundrobin=(roundrobin+1)%num_processes;
my_process=roundrobin; /* the process allocated by this thread */
++num_busy; /* the child process has been allocated */
busy[my_process]=1; /* mark the child process as busy */
retval=pthread_mutex_unlock(&mutex);
if(retval) {
errno=retval;
ioerror("pthread_mutex_unlock");
longjmp(c->err, 1);
}
s_log(LOG_DEBUG, "Acquired libwrap process #%d", my_process);
write_fd(ipc_socket[2*my_process], c->opt->servname,
strlen(c->opt->servname)+1, c->local_rfd.fd);
2017-11-15 15:03:25 +01:00
s_read(c, ipc_socket[2*my_process],
(uint8_t *)&result, sizeof result);
2017-03-28 09:58:13 +02:00
s_log(LOG_DEBUG, "Releasing libwrap process #%d", my_process);
retval=pthread_mutex_lock(&mutex);
if(retval) {
errno=retval;
ioerror("pthread_mutex_lock");
longjmp(c->err, 1);
}
busy[my_process]=0; /* mark the child process as free */
--num_busy; /* the child process has been released */
2017-03-28 10:18:03 +02:00
retval=pthread_cond_signal(&cond); /* signal a waiting thread */
if(retval) {
errno=retval;
ioerror("pthread_cond_signal");
longjmp(c->err, 1);
2017-03-28 09:58:13 +02:00
}
retval=pthread_mutex_unlock(&mutex);
if(retval) {
errno=retval;
ioerror("pthread_mutex_unlock");
longjmp(c->err, 1);
}
s_log(LOG_DEBUG, "Released libwrap process #%d", my_process);
} else
2017-11-15 15:03:25 +01:00
#endif /* USE_LIBWRAP_POOL */
2017-03-28 09:58:13 +02:00
{ /* use original, synchronous libwrap calls */
2017-11-15 15:03:25 +01:00
stunnel_write_lock(&stunnel_locks[LOCK_LIBWRAP]);
2017-03-28 09:58:13 +02:00
result=check(c->opt->servname, c->local_rfd.fd);
2017-11-15 15:03:25 +01:00
stunnel_write_unlock(&stunnel_locks[LOCK_LIBWRAP]);
2017-03-28 09:58:13 +02:00
}
if(!result) {
s_log(LOG_WARNING, "Service [%s] REFUSED by libwrap from %s",
c->opt->servname, accepted_address);
s_log(LOG_DEBUG, "See hosts_access(5) manual for details");
longjmp(c->err, 1);
}
s_log(LOG_DEBUG, "Service [%s] permitted by libwrap from %s",
c->opt->servname, accepted_address);
}
2017-11-15 15:03:25 +01:00
NOEXPORT int check(char *name, int fd) {
2017-03-28 09:58:13 +02:00
struct request_info request;
request_init(&request, RQ_DAEMON, name, RQ_FILE, fd, 0);
fromhost(&request);
return hosts_access(&request);
}
2017-11-15 15:03:25 +01:00
#ifdef USE_LIBWRAP_POOL
2017-03-28 09:58:13 +02:00
2017-11-15 15:03:25 +01:00
NOEXPORT ssize_t read_fd(SOCKET fd, void *ptr, size_t nbytes, SOCKET *recvfd) {
2017-03-28 09:58:13 +02:00
struct msghdr msg;
struct iovec iov[1];
ssize_t n;
#ifdef HAVE_MSGHDR_MSG_CONTROL
union {
struct cmsghdr cm;
char control[CMSG_SPACE(sizeof(int))];
} control_un;
struct cmsghdr *cmptr;
msg.msg_control=control_un.control;
msg.msg_controllen=sizeof control_un.control;
#else
int newfd;
msg.msg_accrights=(caddr_t)&newfd;
msg.msg_accrightslen=sizeof(int);
#endif
msg.msg_name=NULL;
msg.msg_namelen=0;
iov[0].iov_base=ptr;
iov[0].iov_len=nbytes;
msg.msg_iov=iov;
msg.msg_iovlen=1;
2017-11-15 15:03:25 +01:00
*recvfd=INVALID_SOCKET; /* descriptor was not passed */
2017-03-28 09:58:13 +02:00
n=recvmsg(fd, &msg, 0);
if(n<=0)
return n;
#ifdef HAVE_MSGHDR_MSG_CONTROL
cmptr=CMSG_FIRSTHDR(&msg);
if(!cmptr || cmptr->cmsg_len!=CMSG_LEN(sizeof(int)))
return n;
if(cmptr->cmsg_level!=SOL_SOCKET) {
s_log(LOG_ERR, "control level != SOL_SOCKET");
return -1;
}
if(cmptr->cmsg_type!=SCM_RIGHTS) {
s_log(LOG_ERR, "control type != SCM_RIGHTS");
return -1;
}
memcpy(recvfd, CMSG_DATA(cmptr), sizeof(int));
#else
if(msg.msg_accrightslen==sizeof(int))
*recvfd=newfd;
#endif
return n;
}
2017-11-15 15:03:25 +01:00
NOEXPORT ssize_t write_fd(int fd, void *ptr, size_t nbytes, int sendfd) {
2017-03-28 09:58:13 +02:00
struct msghdr msg;
struct iovec iov[1];
#ifdef HAVE_MSGHDR_MSG_CONTROL
union {
struct cmsghdr cm;
char control[CMSG_SPACE(sizeof(int))];
} control_un;
struct cmsghdr *cmptr;
msg.msg_control=control_un.control;
msg.msg_controllen=sizeof control_un.control;
cmptr=CMSG_FIRSTHDR(&msg);
cmptr->cmsg_len=CMSG_LEN(sizeof(int));
cmptr->cmsg_level=SOL_SOCKET;
cmptr->cmsg_type=SCM_RIGHTS;
memcpy(CMSG_DATA(cmptr), &sendfd, sizeof(int));
#else
msg.msg_accrights=(caddr_t)&sendfd;
msg.msg_accrightslen=sizeof(int);
#endif
msg.msg_name=NULL;
msg.msg_namelen=0;
iov[0].iov_base=ptr;
iov[0].iov_len=nbytes;
msg.msg_iov=iov;
msg.msg_iovlen=1;
return sendmsg(fd, &msg, 0);
}
2017-11-15 15:03:25 +01:00
#endif /* USE_LIBWRAP_POOL */
2017-03-28 09:58:13 +02:00
#endif /* USE_LIBWRAP */
/* end of libwrap.c */