/*
**
** Copyright (c) 2002,2003 Dave McMurtrie
** Copyright (c) 2004 Martin Blapp
**
** This file is part of pop3proxy, a descendant of Dave McMurtrie's pop3proxy.
**
** pop3proxy 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.
**
** pop3proxy 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 pop3proxy; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
**
**
** Facility:
**
** main.c
**
** Abstract:
**
** The main source module for the IMAP proxy server. This source
** module contains routines to handle server initialization
** and the main server loop.
**
** Authors:
**
** Dave McMurtrie <davemcmurtrie@hotmail.com>
**
** RCS:
**
** $Source: /afs/pitt.edu/usr12/dgm/work/IMAP_Proxy/src/RCS/main.c,v $
** $Id: main.c,v 1.17 2003/10/10 15:07:02 dgm Exp $
**
** Modification History:
**
** $Log: main.c,v $
** Revision 1.17 2003/10/10 15:07:02 dgm
** Patch by Ken Murchison <ken@oceana.com> applied. Discard "SASL"
** capability if advertised by the server. This is a new extension
** that will be supported in Cyrus v2.2.2.
**
** Revision 1.16 2003/10/09 13:03:52 dgm
** Added configurable tcp keepalive support. Added retry logic for
** initial socket connection to imap server in SetBannerAndCapability()
** so it won't just fatally exit on ECONNREFUSED (submitted by Gary
** Mills <mills@cc.UManitoba.CA>).
**
** Revision 1.15 2003/07/14 16:39:58 dgm
** Applied patch by William Yodlowsky <wyodlows@andromeda.rutgers.edu>
** to allow TLS to work on machines without /dev/random.
**
** Applied patch by Gary Windham <windhamg@email.arizona.edu> to add
** tcp wrappers support.
**
** Revision 1.14 2003/05/20 19:04:23 dgm
** Comment changes only.
**
** Revision 1.13 2003/05/15 11:34:34 dgm
** Patch by Ken Murchison <ken@oceana.com> to clean up build process:
** Conditionally include sys/param.h instead of defining MAXPATHLEN.
**
** Revision 1.12 2003/05/13 14:19:00 dgm
** Changed all uses of AF_INET constant to PF_INET.
**
** Revision 1.11 2003/05/13 11:40:16 dgm
** Patches by Ken Murchison <ken@oceana.com> to clean up build process.
**
** Revision 1.10 2003/05/06 12:12:21 dgm
** Applied patches by Ken Murchison <ken@oceana.com> to include SSL
** support.
**
** Revision 1.9 2003/04/16 12:15:52 dgm
** Added support for syslog configuration.
** Removed a few ifdef LINUXs by always storing tcp service port as
** network short.
**
** Revision 1.8 2003/02/20 12:55:03 dgm
** SetBannerAndCapability() now checks to see if the server supports
** UNSELECT and sets a flag in the global proxy config struct.
**
** Revision 1.7 2003/02/19 13:01:40 dgm
** Changes to SetBannerAndCapability() to strip out unsupported AUTH=
** mechanisms from the capability string.
**
** Revision 1.6 2003/01/27 13:58:18 dgm
** patch by Frode Nordahl <frode@powertech.no> to allow
** compilation on Linux platforms.
**
** Revision 1.5 2002/12/17 14:25:14 dgm
** Added support for global configuration structure.
** Modified supported command line arguments.
** Minor bugfixes from Gary Mills incorporated.
**
** Revision 1.4 2002/09/06 13:33:05 dgm
** Added code to ignore SIGPIPE and SIGHUP.
**
** Revision 1.3 2002/08/30 17:08:35 dgm
** oops. forgot to initialize the trace mutex...
**
** Revision 1.2 2002/08/28 15:56:24 dgm
** replaced all internal logging calls with standard syslog calls.
** Changed call to setrlimit such that it's now done dynamically,
** based on the max number of connections we want to allow.
**
** Revision 1.1 2002/07/03 12:07:51 dgm
** Initial revision
**
**
*/
static char *rcsId = "$Id: main.c,v 1.17 2003/10/10 15:07:02 dgm Exp $";
static char *rcsSource = "$Source: /afs/pitt.edu/usr12/dgm/work/IMAP_Proxy/src/RCS/main.c,v $";
static char *rcsAuthor = "$Author: dgm $";
#define _REENTRANT
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <string.h>
#include <errno.h>
#include <netdb.h>
#include "common.h"
#include "pop3proxy.h"
#include <pthread.h>
#include <sys/resource.h>
#include <sys/mman.h>
#include <pwd.h>
#include <syslog.h>
#include <signal.h>
#if HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#ifdef HAVE_LIBWRAP
#include <tcpd.h>
#endif
/*
* Global variables. Many of these things are global just as an optimization.
* For example, there's no reason to have to do a hostname lookup every
* single time we want to connect to the imap server. We do it once and
* store it globally.
*/
char Banner[BUFSIZE]; /* banner line returned from IMAP svr */
unsigned int BannerLen;
char Capability[BUFSIZE]; /* IMAP capability line from server */
unsigned int CapabilityLen;
char Epoplist[BUFSIZE];
unsigned int EpoplistLen;
ISD_Struct ISD; /* global imap server descriptor */
ICC_Struct *ICC_free; /* ICC free listhead */
ICC_Struct *ICC_HashTable[ HASH_TABLE_SIZE ];
IMAPCounter_Struct *IMAPCount; /* global imap counter struct */
pthread_mutex_t mp; /* "main" mutex used for ICC sync */
pthread_mutex_t trace; /* mutex used for username tracing */
char TraceUser[MAXUSERNAMELEN]; /* username we want to trace */
int Tracefd; /* fd of our trace file (always open) */
ProxyConfig_Struct PC_Struct; /* Global configuration data */
#if HAVE_LIBSSL
#if USE_IMAP
SSL_CTX *tls_ctx;
static int verify_depth = 5;
static int verify_error = X509_V_OK;
static int verify_callback( int, X509_STORE_CTX *);
static int set_cert_stuff( SSL_CTX *, const char *, const char * );
#endif
#endif
#ifdef HAVE_LIBWRAP
int allow_severity = LOG_DEBUG;
int deny_severity = LOG_ERR;
char *service;
#endif
/*
* Internal Prototypes
*/
static int SetBannerAndCapability( void );
static void ServerInit( void );
static void Usage( void );
int main( int argc, char *argv[] )
{
char *fn = "main()";
char f_randfile[ PATH_MAX ];
int listensd; /* socket descriptor we'll bind to */
int clientsd; /* incoming socket descriptor */
int addrlen;
struct sockaddr_in srvaddr;
struct sockaddr_in cliaddr;
pthread_t ThreadId; /* thread id of each incoming conn */
pthread_t RecycleThread; /* used just for the recycle thread */
pthread_attr_t attr; /* generic thread attribute struct */
int rc, i, fd;
pid_t pid; /* used just for a fork call */
struct linger lingerstruct; /* for the socket reuse stuff */
int flag; /* for the socket reuse stuff */
ICC_Struct *ICC_tptr;
extern char *optarg;
extern int optind;
char ConfigFile[ MAXPATHLEN ]; /* path to our config file */
#ifdef HAVE_LIBWRAP
struct request_info r; /* request struct for libwrap */
#endif
flag = 1;
ConfigFile[0] = '\0';
/*
* Ignore signals we don't want to die from but we don't care enough
* about to catch.
*/
signal( SIGPIPE, SIG_IGN );
signal( SIGHUP, SIG_IGN );
while (( i = getopt( argc, argv, "f:h" ) ) != EOF )
{
switch( i )
{
case 'f':
/* user specified a config filename */
strncpy( ConfigFile, optarg, sizeof ConfigFile -1 );
syslog( LOG_INFO, "%s: Using configuration file '%s'",
fn, ConfigFile );
break;
case 'h':
Usage();
exit( 0 );
case '?':
Usage();
exit( 1 );
}
}
/*
* Make sure we know which config file to use and then set our config
* options.
*/
if ( ! ConfigFile[0] )
{
strncpy( ConfigFile, DEFAULT_CONFIG_FILE, sizeof ConfigFile -1 );
syslog( LOG_INFO, "%s: Using default configuration file '%s'.",
fn, ConfigFile );
}
SetConfigOptions( ConfigFile );
SetLogOptions();
#ifdef HAVE_LIBWRAP
/*
* Set our tcpd service name
*/
if (service = strrchr(argv[0], '/'))
service++;
else
service = argv[0];
#endif
/*
* Initialize some stuff.
*/
rc = pthread_mutex_init(&mp, NULL);
if ( rc )
{
syslog(LOG_ERR, "%s: pthread_mutex_init() returned error [%d] initializing main mutex. Exiting.", fn, rc );
exit( 1 );
}
rc = pthread_mutex_init(&trace, NULL);
if ( rc )
{
syslog(LOG_ERR, "%s: pthread_mutex_init() returned error [%d] initializing trace mutex. Exiting.", fn, rc );
exit( 1 );
}
TraceUser[0] = '\0';
syslog( LOG_INFO, "%s: Allocating %d IMAP connection structures.",
fn, PC_Struct.cache_size );
ICC_free = malloc( ( sizeof ( ICC_Struct ) )
* PC_Struct.cache_size );
if ( ! ICC_free )
{
syslog(LOG_ERR, "%s: malloc() failed to allocate [%d] IMAPConnectionContext structures: %s", fn, PC_Struct.cache_size, strerror( errno ) );
exit( 1 );
}
memset( ICC_free, 0, sizeof ( ICC_Struct ) * PC_Struct.cache_size );
ICC_tptr = ICC_free;
/*
* Bug fixed by Gary Mills <mills@cc.UManitoba.CA>. I was pre-incrementing
* ICC_tptr and then assigning. I guess gcc evaluates the expression
* incorrectly, since I never had a problem with this. Gary had the
* problem with cc, so it's fixed here.
*/
for ( i = 0; i < PC_Struct.cache_size - 1; i++ )
{
ICC_tptr->next = ICC_tptr + 1;
ICC_tptr++;
}
memset( ICC_HashTable, 0, sizeof ICC_HashTable );
ServerInit();
/* detach from our parent if necessary */
if (! (getppid() == 1) )
{
if ( (pid = fork()) < 0)
{
syslog(LOG_ERR, "%s: initial call to fork() failed: %s", fn, strerror(errno));
exit( 1 );
}
else if ( pid > 0)
{
exit( 0 );
}
if (setsid() == -1)
{
syslog(LOG_WARNING, "%s: setsid() failed: %s",
fn, strerror(errno));
}
if ( (pid = fork()) < 0)
{
syslog(LOG_ERR, "%s: secondary call to fork() failed: %s", fn,
strerror(errno));
exit( 1 );
}
else if ( pid > 0)
{
exit( 0 );
}
}
if ( SetBannerAndCapability() )
{
syslog(LOG_ERR, "%s: Failed to get banner string and capability from imap server. Exiting.", fn);
exit( 1 );
}
if ( PC_Struct.login_disabled )
{
#if HAVE_LIBSSL
#if USE_IMAP
if ( PC_Struct.support_starttls )
{
/* Initialize SSL_CTX */
SSL_library_init();
/* Need to seed PRNG, too! */
if ( RAND_egd( RAND_file_name( f_randfile, sizeof( f_randfile ) ) ) < 0)
{
/* Not an EGD, so read and write it. */
if ( RAND_load_file( f_randfile, -1 ) )
RAND_write_file( f_randfile );
}
SSL_load_error_strings();
tls_ctx = SSL_CTX_new( TLSv1_client_method() );
if ( tls_ctx == NULL )
{
syslog(LOG_ERR, "%s: Failed to create new SSL_CTX. Exiting.", fn);
exit( 1 );
}
/* Work around all known bugs */
SSL_CTX_set_options( tls_ctx, SSL_OP_ALL );
if ( ! SSL_CTX_load_verify_locations( tls_ctx,
PC_Struct.tls_ca_file,
PC_Struct.tls_ca_path ) ||
! SSL_CTX_set_default_verify_paths( tls_ctx ) )
{
syslog(LOG_ERR, "%s: Failed to load CA data. Exiting.", fn);
exit( 1 );
}
if ( ! set_cert_stuff( tls_ctx,
PC_Struct.tls_cert_file,
PC_Struct.tls_key_file ) )
{
syslog(LOG_ERR, "%s: Failed to load cert/key data. Exiting.", fn);
exit( 1 );
}
SSL_CTX_set_verify(tls_ctx, SSL_VERIFY_NONE, verify_callback);
}
else
#endif /* USE_IMAP */
#endif /* HAVE_LIBSSL */
{
/* We're screwed! We won't be able to login without SASL */
syslog(LOG_ERR, "%s: IMAP server has LOGINDISABLED and we can't do STARTTLS. Exiting.", fn);
exit( 1 );
}
}
memset( (char *) &srvaddr, 0, sizeof srvaddr );
srvaddr.sin_family = PF_INET;
srvaddr.sin_addr.s_addr = htonl(INADDR_ANY);
syslog(LOG_ERR, "%s: Binding to tcp port %d", fn, PC_Struct.listen_port );
srvaddr.sin_port = htons(PC_Struct.listen_port);
listensd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if ( listensd == -1 )
{
syslog(LOG_ERR, "%s: socket() failed: %s", fn, strerror(errno));
exit( 1 );
}
setsockopt(listensd, SOL_SOCKET, SO_REUSEADDR, (void *)&flag,
sizeof(flag));
lingerstruct.l_onoff = 1;
lingerstruct.l_linger = 5;
setsockopt(listensd, SOL_SOCKET, SO_LINGER, (void *)&lingerstruct,
sizeof(lingerstruct));
if ( PC_Struct.send_tcp_keepalives )
{
lingerstruct.l_onoff = 1;
syslog( LOG_INFO, "%s: Enabling SO_KEEPALIVE.", fn );
setsockopt( listensd, SOL_SOCKET, SO_KEEPALIVE, (void *)&lingerstruct.l_onoff, sizeof lingerstruct.l_onoff );
}
if ( bind(listensd, (struct sockaddr *)&srvaddr, sizeof( srvaddr ) ) < 0 )
{
syslog(LOG_ERR, "%s: bind() failed: %s", fn, strerror(errno) );
exit( 1 );
}
/*
* Create and mmap() our stat file while we're still root. Since it's
* configurable, we want to make sure we do this as root so there's the
* greatest possibility that we'll have permission to write where we
* need to.
*/
syslog( LOG_INFO, "%s: Using global statistics file '%s'", fn,
PC_Struct.stat_filename );
fd = open( PC_Struct.stat_filename, O_RDWR | O_CREAT, S_IREAD | S_IWRITE );
if ( fd == -1 )
{
syslog(LOG_ERR, "%s: open() failed for '%s': %s -- Exiting.", fn,
PC_Struct.stat_filename, strerror( errno ) );
exit( 1 );
}
if ( ( ftruncate( fd, sizeof( IMAPCounter_Struct ) ) ) == -1 )
{
syslog(LOG_ERR, "%s: ftruncate() failed: %s -- Exiting.",
fn, strerror( errno ) );
exit( 1 );
}
IMAPCount = ( IMAPCounter_Struct *)mmap( 0, sizeof( IMAPCounter_Struct ),
PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0 );
if ( IMAPCount == MAP_FAILED )
{
syslog(LOG_ERR, "%s: mmap() failed: %s -- Exiting.",
fn, strerror( errno ) );
exit( 1 );
}
memset( IMAPCount, 0, sizeof( IMAPCounter_Struct ) );
IMAPCount->StartTime = time( 0 );
IMAPCount->CountTime = time( 0 );
if ( BecomeNonRoot() )
exit( 1 );
/* some misc thread setup */
rc = pthread_attr_init( &attr );
if ( rc )
{
syslog(LOG_ERR, "%s: pthread_attr_init() failed: [%d]\n", fn, rc);
exit( 1 );
}
rc = pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_DETACHED );
if ( rc )
{
syslog(LOG_ERR, "%s: pthread_attr_setdetachstate() failed: [%d]\n",
fn, rc);
exit( 1 );
}
/* launch a recycle thread before we loop */
pthread_create( &RecycleThread, &attr, (void *)ICC_Recycle_Loop, NULL );
syslog(LOG_INFO, "%s: Launched ICC recycle thread with id %d",
fn, RecycleThread );
/*
* Now start listening and accepting connections.
*/
if ( listen(listensd, MAX_CONN_BACKLOG) < 0)
{
syslog( LOG_ERR, "%s: listen() failed: %s -- Exiting",
fn, strerror(errno));
exit( 1 );
}
syslog( LOG_INFO, "%s: Normal server startup.", fn );
/*
* Main server loop
*/
for ( ;; )
{
/*
* Bug fixed by Gary Mills <mills@cc.UManitoba.CA>. I forgot
* to initialize addrlen.
*/
addrlen = sizeof cliaddr;
clientsd = accept( listensd, (struct sockaddr *)&cliaddr, &addrlen );
if ( clientsd == -1 )
{
syslog(LOG_WARNING, "%s: accept() failed: %s -- retrying",
fn, strerror(errno));
sleep( 1 );
continue;
}
#ifdef HAVE_LIBWRAP
request_init(&r, RQ_DAEMON, service, 0);
request_set(&r, RQ_FILE, clientsd, 0);
sock_host(&r);
if (!hosts_access(&r))
{
shutdown(clientsd, SHUT_RDWR);
close(clientsd);
syslog(deny_severity, "refused connection from %s", eval_client(&r));
continue;
}
#endif
IMAPCount->TotalClientConnectionsAccepted++;
IMAPCount->CurrentClientConnections++;
if ( IMAPCount->CurrentClientConnections >
IMAPCount->PeakClientConnections )
IMAPCount->PeakClientConnections = IMAPCount->CurrentClientConnections;
pthread_create( &ThreadId, &attr, (void *)HandleRequest, (void *)clientsd );
}
}
/*
* Function definitions.
*/
/*++
* Function: Usage
*
* Purpose: Display a usage string to stdout
*
* Parameters: None.
*
* Returns: nada
*
* Authors: Dave McMurtrie <davemcmurtrie@hotmail.com>
*
* Notes:
*--
*/
void Usage( void )
{
printf("Usage: %s [-f config filename] [-h]\n", PGM );
return;
}
/*++
* Function: ServerInit
*
* Purpose: Initialize some stuff. Set some global variables.
*
* Parameters: none
*
* Returns: nada -- exits on error
*
* Authors: Dave McMurtrie <davemcmurtrie@hotmail.com>
*
* Notes: relies on global copy of ProxyConfig_Struct "PC_Struct"
*--
*/
static void ServerInit( void )
{
char *fn = "ServerInit()";
struct hostent *hp;
struct servent *sp;
struct rlimit rl;
int rc;
struct passwd *pw;
/* open the global trace file and make proc_username own it */
syslog( LOG_INFO, "%s: Using '%s' for global protocol logging file.",
fn, PC_Struct.protocol_log_filename );
Tracefd = open( PC_Struct.protocol_log_filename,
O_RDWR | O_CREAT | O_TRUNC, 0600 );
if ( Tracefd == -1 )
{
syslog(LOG_ERR, "%s: open() failed for '%s': %s -- Exiting.", fn,
PC_Struct.protocol_log_filename, strerror( errno ) );
exit( 1 );
}
if ( ( pw = getpwnam( PC_Struct.proc_username ) ) == NULL )
{
syslog(LOG_ERR, "%s: getpwnam() failed for user '%s' -- Exiting.", fn,
PC_Struct.proc_username );
exit( 1 );
}
rc = chown( PC_Struct.protocol_log_filename, pw->pw_uid, pw->pw_gid );
if ( rc )
{
syslog(LOG_ERR, "%s: Failed to set ownership of file '%s' to '%s': %s -- Exiting.", fn, PC_Struct.protocol_log_filename, PC_Struct.proc_username, strerror( errno ) );
exit( 1 );
}
/*
* increase the number of open file descriptors we're allowed. Base
* This number on the number of simultaneous connections we allow.
* Also allow stdin, stdout, stderr and a few misc pipes and doors.
*/
rl.rlim_cur = ( PC_Struct.cache_size * 2 ) + 10;
rl.rlim_max = ( PC_Struct.cache_size * 2 ) + 10;
rc = setrlimit( RLIMIT_NOFILE, &rl );
if ( rc )
{
syslog(LOG_ERR, "%s: setrlimit() failed to set max number of open file descriptors to %d: %s", fn, ( PC_Struct.cache_size * 2 + 10), strerror( errno ) );
exit(1);
}
/* grab a host entry for the imap server. */
syslog( LOG_INFO, "%s: proxying to POP3 server '%s'.", fn,
PC_Struct.server_hostname );
hp = gethostbyname( PC_Struct.server_hostname );
if ( !hp )
{
syslog(LOG_ERR, "%s: gethostbyname() failed to resolve hostname of remote IMAP server: %s", fn, strerror(errno) );
exit(1);
}
memcpy( &ISD.host, hp, sizeof(struct hostent) );
syslog(LOG_INFO, "%s: proxying to POP3 port %d",
fn, PC_Struct.server_port );
ISD.serv.s_port = htons(PC_Struct.server_port);
/*
* fill in the address family, the host address, and the
* service port of our global socket address structure
*/
ISD.srv.sin_family = PF_INET;
memcpy( &ISD.srv.sin_addr.s_addr, ISD.host.h_addr, ISD.host.h_length );
ISD.srv.sin_port = ISD.serv.s_port;
}
/*++
* Function: SetBannerAndCapability
*
* Purpose: Connect to an IMAP server as a client and fetch the initial
* banner string and the output from a CAPABILITY command.
*
* Parameters: none
*
* Returns: 0 on success
* -1 on failure
*
* Authors: Dave McMurtrie <davemcmurtrie@hotmail.com>
*
* Notes: All AUTH mechanisms will be stripped from the capability
* string. AUTH=LOGIN will be added.
* The support_unselect flag in the global copy of the
* ProxyConfig struct will be set in this function depending on
* whether the server supports UNSELECT or not.
*--
*/
static int SetBannerAndCapability( void )
{
int sd;
ITD_Struct itd;
ICD_Struct conn;
int BytesRead;
int Processed;
char *fn = "SetBannerAndCapability()";
char *CP;
int NumRef = 0;
/* initialize some stuff */
memset( &itd, 0, sizeof itd );
for ( ;; )
{
sd = socket( PF_INET, SOCK_STREAM, IPPROTO_TCP );
if ( sd == -1 )
{
syslog(LOG_ERR, "%s: socket() failed: %s", fn, strerror(errno) );
return( -1 );
}
if ( connect( sd, (struct sockaddr *)&ISD.srv, sizeof(ISD.srv) ) == -1 )
{
syslog(LOG_ERR, "%s: connect() to imap server on socket [%d] failed: %s", fn, sd, strerror(errno));
close( sd );
if ( errno == ECONNREFUSED && ++NumRef < 10 )
{
sleep( 60 ); /* POP3 server may not be started yet. */
continue;
}
return( -1 );
}
break; /* Success */
}
memset( &conn, 0, sizeof ( ICD_Struct ) );
itd.conn = &conn;
itd.conn->sd = sd;
/*
* The first thing we get back from the server should be the
* banner string.
*/
BytesRead = IMAP_Line_Read( &itd );
if ( BytesRead == -1 )
{
close( itd.conn->sd );
return( -1 );
}
if ( sizeof Banner < BytesRead )
{
syslog(LOG_ERR, "%s: Storing %d byte banner string from POP3 server would cause buffer overflow.", fn, BytesRead );
close( itd.conn->sd );
return( -1 );
}
memcpy( Banner, itd.ReadBufline, BytesRead );
BannerLen = BytesRead;
/*
* See if the string we got back starts with "+OK" by comparing the
* first 4 characters of the buffer.
*/
if ( strncasecmp( Banner, IMAP_UNTAGGED_OK, strlen(IMAP_UNTAGGED_OK)) )
{
syslog(LOG_ERR, "%s: Unexpected response from imap server on initial connection: %s", fn, Banner);
close( itd.conn->sd );
return( -1 );
}
/*
* Then get the capability list if available
*/
if (IMAP_Write( itd.conn, "CAPA\r\n", strlen("CAPA\r\n")) == -1) {
syslog(LOG_ERR, "%s: IMAP_Write() failed: %s", fn, strerror(errno) );
close( itd.conn->sd );
return( -1 );
}
Capability[0] = '\0';
for (;;) {
BytesRead = IMAP_Line_Read( &itd );
if ( BytesRead == -1 )
continue;
strlcat(Capability, itd.ReadBufline, sizeof(Capability));
if (!memcmp(itd.ReadBufline, "+OK", 3))
strlcat(Capability, "PROXY\r\n", sizeof(Capability));
if (!memcmp(itd.ReadBufline, ".\r\n", 3))
break;
}
CapabilityLen = strlen(Capability);
/*
* Get the epop list if available
*/
if (IMAP_Write( itd.conn, "EPOP\r\n", strlen("EPOP\r\n")) == -1) {
syslog(LOG_ERR, "%s: IMAP_Write() failed: %s", fn, strerror(errno) );
close( itd.conn->sd );
return( -1 );
}
Epoplist[0] = '\0';
for (;;) {
BytesRead = IMAP_Line_Read( &itd );
if ( BytesRead == -1 )
continue;
strlcat(Epoplist, itd.ReadBufline, sizeof(Epoplist));
if (!memcmp(itd.ReadBuf, "-ERR", 4))
break;
if (!memcmp(itd.ReadBuf, ".\r\n", 3))
break;
}
EpoplistLen = strlen(Epoplist);
/* Be nice and logout */
if ( IMAP_Write( itd.conn, "QUIT\r\n", strlen("QUIT\r\n") ) == -1 )
{
syslog(LOG_WARNING, "%s: POP3_Write() failed on LOGOUT: %s -- Returning success anyway.", fn, strerror(errno) );
return( 0 );
}
/* read the final OK logout */
BytesRead = IMAP_Line_Read( &itd );
if ( BytesRead == -1 )
{
syslog(LOG_WARNING, "%s: IMAP_Line_Read() failed on LOGOUT. Returning success anyway.", fn );
}
close( itd.conn->sd );
return( 0 );
}
#if HAVE_LIBSSL
#if USE_IMAP
/* taken from OpenSSL apps/s_cb.c */
static int verify_callback(int ok, X509_STORE_CTX * ctx)
{
char buf[256];
X509 *err_cert;
int err;
int depth;
syslog(LOG_ERR,"Doing a peer verify");
err_cert = X509_STORE_CTX_get_current_cert(ctx);
err = X509_STORE_CTX_get_error(ctx);
depth = X509_STORE_CTX_get_error_depth(ctx);
X509_NAME_oneline(X509_get_subject_name(err_cert), buf, sizeof(buf));
if (ok==0)
{
syslog(LOG_ERR, "verify error:num=%d:%s", err,
X509_verify_cert_error_string(err));
if (verify_depth >= depth) {
ok = 0;
verify_error = X509_V_OK;
} else {
ok = 0;
verify_error = X509_V_ERR_CERT_CHAIN_TOO_LONG;
}
}
switch (ctx->error) {
case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert), buf, sizeof(buf));
syslog(LOG_NOTICE, "issuer= %s", buf);
break;
case X509_V_ERR_CERT_NOT_YET_VALID:
case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
syslog(LOG_NOTICE, "cert not yet valid");
break;
case X509_V_ERR_CERT_HAS_EXPIRED:
case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
syslog(LOG_NOTICE, "cert has expired");
break;
}
return (ok);
}
static int set_cert_stuff(SSL_CTX * ctx,
const char *cert_file, const char *key_file)
{
if (cert_file != NULL) {
if (SSL_CTX_use_certificate_file(ctx, cert_file,
SSL_FILETYPE_PEM) <= 0) {
syslog(LOG_ERR, "unable to get certificate from '%s'", cert_file);
return (0);
}
if (key_file == NULL)
key_file = cert_file;
if (SSL_CTX_use_PrivateKey_file(ctx, key_file,
SSL_FILETYPE_PEM) <= 0) {
syslog(LOG_ERR, "unable to get private key from '%s'", key_file);
return (0);
}
/* Now we know that a key and cert have been set against
* the SSL context */
if (!SSL_CTX_check_private_key(ctx)) {
syslog(LOG_ERR,
"Private key does not match the certificate public key");
return (0);
}
}
return (1);
}
#endif /* USE_IMAP */
#endif /* HAVE_LIBSSL */
/*
* _________
* / |
* / |
* / ______|
* / / ________
* | | | /
* | | |_____/
* | | ______
* | | | \
* | | |______\
* \ \_______
* \ |
* \ |
* \_________|
*/
syntax highlighted by Code2HTML, v. 0.9.1