Esempio n. 1
0
/*++
 * Function:     Send_Cached_Select_Response
 *
 * Purpose:      Send cached SELECT server response data back to a client.
 *
 * Parameters:   ptr to ITD -- client transaction descriptor
 *               ptr to ISC -- imap select cache structure
 *               ptr to char -- client tag for response
 *
 * Returns:      0 on success
 *               -1 on failure
 *
 * Authors:      Dave McMurtrie <*****@*****.**>
 *
 * Notes:
 *--
 */
static int Send_Cached_Select_Response( ITD_Struct *Client,
					ISC_Struct *ISC,
					char *Tag )
{
    char *fn = "Send_Cached_Select_Response()";
    char SendBuf[ BUFSIZE ];

    if ( IMAP_Write( Client->conn, ISC->SelectString, 
		     strlen( ISC->SelectString ) ) == -1 )
    {
	syslog( LOG_WARNING, "%s: Failed to send cached SELECT string to client on sd [%d]: %s", fn, Client->conn->sd, strerror( errno ) );
	return( -1 );
    }
    
    snprintf( SendBuf, sizeof SendBuf - 1, "%s %s", Tag, 
	      ISC->SelectStatus );
    
    if ( IMAP_Write( Client->conn, SendBuf, strlen( SendBuf ) ) == -1 )
    {
	syslog( LOG_WARNING, "%s: Failed to send cached SELECT status to client on sd [%d]: %s", fn, Client->conn->sd, strerror( errno ) );
	return( -1 );
    }

    return( 0 );
    
}
Esempio n. 2
0
/*++
 * Function:	cmd_newlog
 *
 * Purpose:	Clear the proxy trace log file.
 *
 * Parameters:	ptr to ITD_Struct for client connection.
 *              char ptr to Tag sent with this command.
 *
 * Returns:	0 on success
 *		-1 on failure
 *
 * Authors:     Dave McMurtrie <*****@*****.**>
 *--
 */
static int cmd_newlog( ITD_Struct *itd, char *Tag )
{
    char *fn = "cmd_newlog";
    char SendBuf[BUFSIZE];
    unsigned int BufLen = BUFSIZE - 1;
    int rc;
    
    SendBuf[BUFSIZE - 1] = '\0';

    rc = ftruncate( Tracefd, 0 );
    
    if ( rc != 0 )
    {
	syslog(LOG_ERR, "%s: ftruncate() failed: %s", fn, strerror( errno ) );
	snprintf( SendBuf, BufLen, "%s NO internal server error\r\n", Tag );

	if ( IMAP_Write( itd->conn, SendBuf, strlen(SendBuf) ) == -1 )
	{
	    syslog(LOG_WARNING, "%s: IMAP_Write() failed: %s", fn, strerror(errno) );
	    return( -1 );
	}
	
	return( -1 );
    }
    
    /*
     * bugfix.  ftruncate doesn't reset the file pointer...
     */
    rc = lseek( Tracefd, 0, SEEK_SET );
    
    if ( rc < 0 )
    {
	syslog(LOG_ERR, "%s: lseek() failed: %s", fn, strerror( errno ) );
	snprintf( SendBuf, BufLen, "%s NO internal server error\r\n", Tag );
	
	if ( IMAP_Write( itd->conn, SendBuf, strlen(SendBuf) ) == -1 )
	{
	    syslog(LOG_WARNING, "%s: IMAP_Write() failed: %s", fn, strerror(errno) );
	    return( -1 );
	}
	
	return( -1 );
    }

    snprintf( SendBuf, BufLen, "%s OK Completed\r\n", Tag );
    
    if ( IMAP_Write( itd->conn, SendBuf, strlen(SendBuf) ) == -1 )
    {
	syslog(LOG_WARNING, "%s: IMAP_Write() failed: %s", fn, strerror(errno) );
	return( -1 );
    }
    
    return( 0 );
}
Esempio n. 3
0
/*++
 * Function:	cmd_resetcounters
 *
 * Purpose:	Reset the global high-water marks and total counters.
 *
 * Parameters:	ptr to ITD_Struct for client connection.
 *		char ptr to Tag sent with this command.
 *
 * Returns:	0 on success.
 *		-1 on failure.
 *
 * Authors:	Dave McMurtrie <*****@*****.**>
 *
 * Notes:	Always key to remember that we don't take out a mutex
 *		anywhere that we update these global counters.  There's
 *		never a guarantee that they'll be exactly correct but
 *		for the performance penalty we'd pay to make them correct
 *		we just don't care.
 *--
 */
static int cmd_resetcounters( ITD_Struct *itd, char *Tag )
{
    char *fn = "cmd_resetcounters";
    char SendBuf[BUFSIZE];
    unsigned int BufLen = BUFSIZE -1;
    
    SendBuf[BufLen] = '\0';
    
    IMAPCount->CountTime = time( 0 );
    IMAPCount->PeakClientConnections = 0;
    IMAPCount->PeakInUseServerConnections = 0;
    IMAPCount->PeakRetainedServerConnections = 0;
    IMAPCount->TotalClientConnectionsAccepted = 0;
    IMAPCount->TotalServerConnectionsCreated = 0;
    IMAPCount->TotalServerConnectionsReused = 0;
    
    snprintf( SendBuf, BufLen, "%s OK Completed\r\n", Tag );
    
    if ( IMAP_Write( itd->conn, SendBuf, strlen(SendBuf) ) == -1 )
    {
	syslog(LOG_WARNING, "%s: IMAP_Write() failed: %s", fn, strerror(errno) );
	return( -1 );
    }
    
    return( 0 );
}
Esempio n. 4
0
/*++
 * Function:	cmd_dumpicc
 *
 * Purpose:	Dump the contents of all imap connection context structs.
 *
 * Parameters:	ptr to ITD_Struct for client connection.
 *              char ptr to Tag sent with this command.
 *
 * Returns:	0 on success
 *		-1 on failure
 *
 * Authors:     Dave McMurtrie <*****@*****.**>
 *--
 */
static int cmd_dumpicc( ITD_Struct *itd, char *Tag )
{
    char *fn = "cmd_dumpicc";
    char SendBuf[BUFSIZE];
    unsigned int HashIndex;
    ICC_Struct *HashEntry;
    unsigned int BufLen = BUFSIZE - 1;
    
    SendBuf[BUFSIZE - 1] = '\0';
    
    LockMutex( &mp );
    
    for ( HashIndex = 0; HashIndex < HASH_TABLE_SIZE; HashIndex++ )
    {
	HashEntry = ICC_HashTable[ HashIndex ];
	
	while ( HashEntry )
	{
	    snprintf( SendBuf, BufLen, "* %d %s %s\r\n", HashEntry->server_conn->sd,
		      HashEntry->username,
		      ( ( HashEntry->logouttime ) ? "Cached" : "Active" ) );
	    if ( IMAP_Write( itd->conn, SendBuf, strlen(SendBuf) ) == -1 )
	    {
		UnLockMutex( &mp );
		syslog(LOG_WARNING, "%s: IMAP_Write() failed: %s", fn, strerror(errno) );
		return( -1 );
	    }
	    HashEntry = HashEntry->next;
	}
    }
    
    UnLockMutex( &mp );
    
    snprintf( SendBuf, BufLen, "%s OK Completed\r\n", Tag );
    if ( IMAP_Write( itd->conn, SendBuf, strlen(SendBuf) ) == -1 )
    {
	syslog(LOG_WARNING, "%s: IMAP_Write() failed: %s", fn, strerror(errno) );
	return( -1 );
    }
    
    return( 0 );
}
Esempio n. 5
0
/*++
 * Function:	cmd_capability
 *
 * Purpose:	implement the CAPABILITY IMAP command.
 *
 * Parameters:	ptr to ITD_Struct for client connection.
 *
 * Returns:	0 on success
 *		-1 on failure
 *
 * Authors:     Dave McMurtrie <*****@*****.**>
 *--
 */
static int cmd_capability( ITD_Struct *itd, char *Tag )
{
    char *fn = "cmd_capability";
    char SendBuf[BUFSIZE];
    unsigned int BufLen = BUFSIZE - 1;
    
    SendBuf[BUFSIZE - 1] = '\0';
    
    snprintf( SendBuf, BufLen, "%s%s OK Completed\r\n",Capability, Tag );
    if ( IMAP_Write( itd->conn, SendBuf, strlen(SendBuf) ) == -1 )
    {
	syslog(LOG_WARNING, "%s: IMAP_Write() failed: %s", fn, strerror(errno) );
	return( -1 );
    }
    
    return( 0 );
}
Esempio n. 6
0
/*++
 * Function:	cmd_logout
 *
 * Purpose:	implement the LOGOUT IMAP command.
 *
 * Parameters:	ptr to ITD_Struct for client connection.
 *
 * Returns:	0 on success
 *		-1 on failure
 *
 * Authors:     Dave McMurtrie <*****@*****.**>
 *--
 */
static int cmd_logout( ITD_Struct *itd, char *Tag )
{
    char *fn = "cmd_logout";
    char SendBuf[BUFSIZE];
    unsigned int BufLen = BUFSIZE - 1;
    
    SendBuf[BUFSIZE - 1] = '\0';
    
    snprintf( SendBuf, BufLen, "* BYE LOGOUT received\r\n%s OK Completed\r\n",
	      Tag );
    if ( IMAP_Write( itd->conn, SendBuf, strlen(SendBuf) ) == -1 )
    {
	syslog(LOG_WARNING, "%s: IMAP_Write() to client failed on sd [%d]: %s", fn, itd->conn->sd, strerror(errno) );
	return( -1 );
    }
    
    return( 0 );
}
Esempio n. 7
0
/*++
 * Function:	cmd_authenticate_login
 *
 * Purpose:	implement the AUTHENTICATE LOGIN mechanism
 *
 * Parameters:	ptr to ITD_Struct for client connection.
 *              ptr to client tag
 *
 * Returns:	0 on success prior to authentication
 *              1 on success after authentication (we caught a logout)
 *              -1 on failure	
 *
 * Authors:	Dave McMurtrie <*****@*****.**>
 *
 * Notes:
 *--
 */
static int cmd_authenticate_login( ITD_Struct *Client,
				   char *Tag )
{
    char *fn = "cmd_authenticate_login()";
    char SendBuf[BUFSIZE];
    char Username[MAXUSERNAMELEN];
    char EncodedUsername[BUFSIZE];
    char Password[MAXPASSWDLEN];
    char EncodedPassword[BUFSIZE];
    ICD_Struct *conn;
    int rc;
    ITD_Struct Server;
    int BytesRead;
    struct sockaddr_in cli_addr;
    int addrlen;
    char *hostaddr;
    
    unsigned int BufLen = BUFSIZE - 1;
    memset ( &Server, 0, sizeof Server );
    addrlen = sizeof( struct sockaddr_in );
    
    /*
     * send a base64 encoded username prompt to the client.  Note that we're
     * using our Username and EncodedUsername buffers temporarily here to
     * avoid allocating additional buffers.  Keep this in mind for future
     * code modification...
     */
    snprintf( Username, BufLen, "Username:"******"+ %s\r\n", EncodedUsername );
    
    if ( IMAP_Write( Client->conn, SendBuf, strlen(SendBuf) ) == -1 )
    {
	syslog(LOG_ERR, "%s: Unable to send base64 encoded username prompt to client: %s", fn, strerror(errno) );
	return( -1 );
    }

    /*
     * The response from the client should be a base64 encoded version of the
     * username.
     */
    BytesRead = IMAP_Line_Read( Client );
    
    if ( BytesRead == -1 )
    {
	syslog( LOG_NOTICE, "%s: Failed to read base64 encoded username from client on socket %d", fn, Client->conn->sd );
	return( -1 );
    }
    
    /*
     * Easy, but not perfect sanity check.  If the client sent enough data
     * to fill our entire buffer, we're not even going to bother looking at it.
     */
    if ( Client->MoreData ||
	 BytesRead > BufLen )
    {
	syslog( LOG_NOTICE, "%s: Base64 encoded username sent from client on sd %d is too large.", fn, Client->conn->sd );
	return( -1 );
    }
    
    /*
     * copy BytesRead -2 so we don't include the CRLF.
     */
    memcpy( (void *)EncodedUsername, (const void *)Client->ReadBuf, 
	    BytesRead - 2 );
    
    rc = EVP_DecodeBlock( Username, EncodedUsername, BytesRead - 2 );
    Username[rc] = '\0';
    
    /*
     * Same drill all over again, except this time it's for the password.
     */
    snprintf( Password, BufLen, "Password:"******"+ %s\r\n", EncodedPassword );
    
    if ( IMAP_Write( Client->conn, SendBuf, strlen(SendBuf) ) == -1 )
    {
        syslog(LOG_ERR, "%s: Unable to send base64 encoded password prompt to 
client: %s", fn, strerror(errno) );
        return( -1 );
    }
Esempio n. 8
0
/*++
 * Function:	cmd_trace
 *
 * Purpose:	turn on per-user tracing in the proxy server.
 *
 * Parameters:	ptr to ITD_Struct for client connection.
 *              char ptr to Tag sent with this command.
 *              char ptr to the username we want to trace (NULL to turn
 *              off tracing)
 *
 * Returns:	0 on success
 *		-1 on failure
 *
 * Authors:     Dave McMurtrie <*****@*****.**>
 *--
 */
static int cmd_trace( ITD_Struct *itd, char *Tag, char *Username )
{
    char *fn = "cmd_trace";
    char SendBuf[BUFSIZE];
    unsigned int BufLen = BUFSIZE - 1;
    
    SendBuf[BUFSIZE - 1] = '\0';
    
    /*
     * Here are the tracing semantics:
     *
     * Tracing is to be limited to only one user at a time.  This decision was
     * made for a few different reasons.  First, to conserve system resources
     * such as disk space.  Second, to improve overall server performance --
     * tracing will slow a thread down.  Third, so a sysadmin doesn't forget
     * that tracing is turned on for a user (like I commonly do when I enable
     * tracing in cyrus imapd).  Fourth, it's just easier this way.
     */    
    
    LockMutex( &trace );
    
    if ( !Username )
    {
	snprintf( SendBuf, BufLen, "\n\n-----> C= %s PROXY: user tracing disabled. Expect further output until client logout.\n", TraceUser );
	write( Tracefd, SendBuf, strlen( SendBuf ) );
	
	memset( TraceUser, 0, sizeof TraceUser );
	snprintf( SendBuf, BufLen, "%s OK Tracing disabled\r\n", Tag );
	if ( IMAP_Write( itd->conn, SendBuf, strlen(SendBuf) ) == -1 )
	{
	    syslog(LOG_WARNING, "%s: IMAP_Write() failed: %s", fn, strerror(errno) );
	    UnLockMutex( &trace );
	    return( -1 );
	}

	UnLockMutex( &trace );
	return( 0 );
    }

    if ( TraceUser[0] )
    {
	/* guarantee no runaway strings */
	TraceUser[sizeof TraceUser - 1] = '\0';
	snprintf( SendBuf, BufLen, "%s BAD Tracing already enabled for user %s\r\n", Tag, TraceUser );
	if ( IMAP_Write( itd->conn, SendBuf, strlen(SendBuf) ) == -1 )
	{
	    syslog(LOG_WARNING, "%s: IMAP_Write() failed: %s", fn, strerror(errno) );
	    UnLockMutex( &trace );
	    return( -1 );
	}
	
	UnLockMutex( &trace );
	return( 0 );
	
    }
    
    strncpy( TraceUser, Username, sizeof TraceUser - 1 );
    
    snprintf( SendBuf, BufLen, "%s OK Tracing enabled\r\n", Tag );
    if ( IMAP_Write( itd->conn, SendBuf, strlen(SendBuf) ) == -1 )
    {
	syslog(LOG_WARNING, "%s: IMAP_Write() failed: %s", fn, strerror(errno) );
	UnLockMutex( &trace );
	return( -1 );
    }

    snprintf( SendBuf, BufLen, "\n\n-----> C= %s PROXY: user tracing enabled.\n", TraceUser );
    write( Tracefd, SendBuf, strlen( SendBuf ) );
    
    UnLockMutex( &trace );
    return( 0 );
}
Esempio n. 9
0
/*++
 * Function:     Populate_Select_Cache
 *
 * Purpose:      Send a SELECT command to the server and cache the response.
 *
 * Parameters:   ptr to ITD -- server transaction descriptor
 *               ptr to ISC -- the cache structure to populate
 *               ptr to char -- the mailbox name that's being selected
 *               ptr to char -- The select command string from the client.
 *               unsigned int -- the length of the select command
 *
 * Returns:      0 on success
 *               -1 on failure
 *
 * Authors:      Dave McMurtrie <*****@*****.**>
 *
 * Notes:
 *--
 */
static int Populate_Select_Cache( ITD_Struct *Server,
				  ISC_Struct *ISC,
				  char *MailboxName,
				  char *ClientCommand,
				  unsigned int Length )
{
    char *fn = "Populate_Select_Cache()";
    int rc;
    int BytesLeftInBuffer = SELECT_BUF_SIZE;
    char *BufPtr;
    char *CP;
    char *EOS;

    rc = IMAP_Write( Server->conn, ClientCommand, Length );
    
    if ( rc == -1 )
    {
	syslog( LOG_ERR, "%s: Unable to send SELECT command to imap server so can't populate cache.", fn );
	return( -1 );
    }

    BufPtr = ISC->SelectString;
    
    for( ;; )
    {
	if ( Server->LiteralBytesRemaining )
	{
	    syslog( LOG_ERR, "%s: Server response to SELECT command contains unexpected literal data on sd [%d].", fn );
	    /*
	     * Must eat the literal.
	     */
	    while ( Server->LiteralBytesRemaining )
	    {
		IMAP_Literal_Read( Server );
	    }
	    
	    return( -1 );
	}
	
	rc = IMAP_Line_Read( Server );
	
	if ( ( rc == -1 ) || ( rc == 0 ) )
	{
	    syslog( LOG_WARNING, "%s: Unable to read SELECT response from imap server so can't populate cache.", fn );
	    return( -1 );
	}
	
	/*
	 * If it's not untagged data, we're done
	 */
	if ( Server->ReadBuf[0] != '*' )
	    break;
	
	if ( rc >= BytesLeftInBuffer ) 
	{
	    syslog( LOG_WARNING, "%s: Size of SELECT response from server exceeds max cache size of %d bytes.  Unable to cache this response.", fn, SELECT_BUF_SIZE );
	    return( -1 );
	}
	
	memcpy( (void *)BufPtr, (const void *)Server->ReadBuf, rc );
	BytesLeftInBuffer -= rc;
	BufPtr += rc;
    }
    
    /*
     * NULL terminate the buffer that contains the select response.  Note
     * that we used the >= conditional above so we'd leave one byte of
     * space for this NULL
     */
    *BufPtr = '\0';
    
    /*
     * The SELECT output string is filled in.  Now fill in the status.
     */
    CP = memchr( (const void *)Server->ReadBuf, ' ', rc );
    if ( ! CP )
    {
	syslog( LOG_ERR, "%s: Invalid response to SELECT command.  Contains no tokens.", fn );
	return( -1 );
    }
    CP++;
    
    EOS = memchr( (const void *)Server->ReadBuf, '\r', rc );
    if ( ! EOS )
    {
	syslog( LOG_ERR, "%s: Invalid response to SELECT command.  Not CRLF terminated.", fn );
	return( -1 );
    }
    
    *EOS = '\0';
    snprintf( (char *)ISC->SelectStatus, SELECT_STATUS_BUF_SIZE - 1, "%s\r\n",
	      CP );
    *EOS = '\r';

    /*
     * Update the cache time
     */
    ISC->ISCTime = time( 0 );

    strncpy( (char *)ISC->MailboxName, (const char *)MailboxName, MAXMAILBOXNAME - 1 );
    ISC->MailboxName[ MAXMAILBOXNAME - 1 ] = '\0';

    return( 0 );
    
}
Esempio n. 10
0
/*++
 * Function:     Handle_Select_Command
 *
 * Purpose:      The client sent a SELECT command.  Either hit the cache,
 *               or get data from the imap server.
 *
 * Parameters:   ptr to ITD -- client transaction descriptor
 *               ptr to ITD -- server transaction descriptor
 *               ptr to ISC -- imap select cache structure
 *               ptr to char -- The select command string from the client.
 *               unsigned int -- the length of the select command
 *
 * Returns:      0 - The caller should consider the entire SELECT
 *                   transaction to be complete.  This return code does not
 *                   imply successful completion.  Rather, it implies that
 *                   neither the client nor the server should be sending more
 *                   data wrt this transaction.
 *
 *               1 - This return code implies that this function was not able
 *                   to really accomplish anything useful.  The caller should
 *                   attempt to send the SELECT command by an alternate means.
 *                   The SELECT command is still in the client read buffer and
 *                   may be proxied directly to the server without the use of
 *                   this function.
 * 
 *              -1 - A hard failure condition.  The client and server sockets
 *                   should be shut down.
 *
 * Authors:      Dave McMurtrie <*****@*****.**>
 *
 * Notes:        The SELECT command string passed into here will be the
 *               entire command, including the tag.
 *--
 */
extern int Handle_Select_Command( ITD_Struct *Client,
				  ITD_Struct *Server,
				  ISC_Struct *ISC,
				  char *SelectCmd,
				  int SelectCmdLength )
{
    char *fn = "Handle_Select_Command";
    char *Mailbox;
    char *Tag;
    char *CP;

    char Buf[ BUFSIZE ];

    IMAPCount->TotalSelectCommands++;
    
    /*
     * Make a local copy of the select buffer so we can chop it to hell without
     * offending the caller.
     */
    if ( SelectCmdLength >= BUFSIZE )
    {
	IMAPCount->SelectCacheMisses++;
	syslog( LOG_ERR, "%s: Length of SELECT command (%d bytes) would overflow %d byte buffer.", fn, SelectCmdLength, BUFSIZE );
	return( 1 );
    }
    
    memcpy( Buf, SelectCmd, SelectCmdLength );
    Buf[ SelectCmdLength ] = '\0';

    /*
     * NULL terminate the buffer at the end of the mailbox name
     */
    CP = memchr( (const void *)Buf, '\r', SelectCmdLength );
    if ( ! CP )
    {
	IMAPCount->SelectCacheMisses++;
	
	syslog( LOG_ERR, "%s: Sanity check failed!  SELECT command from client sd [%d] has no CRLF after it.", fn, Client->conn->sd );
	return( -1 );
    }
    *CP = '\0';

    Tag = Buf;
    CP = memchr( ( const void * )Buf, ' ', SelectCmdLength );
    
    if ( ! CP )
    {
	IMAPCount->SelectCacheMisses++;
	
	syslog( LOG_ERR, "%s: Sanity check failed!  No tokens found in SELECT command '%s' sent from client sd [%d].", fn, Buf, Client->conn->sd );
	return( 1 );
    }
    
    *CP = '\0';
    CP++;

    Mailbox = memchr( ( const void * )CP, ' ', SelectCmdLength - 
		      ( strlen( Tag ) + 1 ) );
    
    if ( ! Mailbox )
    {
	IMAPCount->SelectCacheMisses++;
	
	syslog( LOG_WARNING, "%s: Protocol error.  Client sd [%d] sent SELECT command with no mailbox name: '%s'", fn, Client->conn->sd, SelectCmd );
	snprintf( Buf, sizeof Buf - 1, "%s BAD missing required argument to SELECT command\r\n", Tag );
	IMAP_Write( Client->conn, Buf, strlen( Buf ) );
	return( 0 );
    }

    Mailbox++;
    
    /*
     * We have a valid SELECT command.  See if we have a cache entry that
     * isn't expired.
     */
    if ( time( 0 ) > ( ISC->ISCTime + SELECT_CACHE_EXP ) )
    {
	/*
	 * The SELECT data that's cached has expired.
	 */
	IMAPCount->SelectCacheMisses++;
	
	if ( Populate_Select_Cache( Server, ISC, Mailbox, SelectCmd, SelectCmdLength ) == -1 )
	{
	    return( 1 );
	}
	
	if ( Send_Cached_Select_Response( Client, ISC, Tag ) == -1 )
	{
	    snprintf( Buf, sizeof Buf - 1, "%s BAD internal proxy server error\r\n", Tag );
	    IMAP_Write( Client->conn, Buf, strlen( Buf ) );
	    return( 0 );
	} 
	
	return( 0 );
    }


    /*
     * Our data isn't expired, but is it the correct mailbox?
     */
    if ( ! strcmp( Mailbox, ISC->MailboxName ) )
    {
	/*
	 * We have the correct mailbox selected and cached already
	 */
	IMAPCount->SelectCacheHits++;
	
	if ( Send_Cached_Select_Response( Client, ISC, Tag ) == -1 )
	{
	    snprintf( Buf, sizeof Buf - 1, "%s BAD internal proxy server error\r\n", Tag );
	    IMAP_Write( Client->conn, Buf, strlen( Buf ) );
	    return( 0 );
	}
	
	return( 0 );
	
    }

    IMAPCount->SelectCacheMisses++;
    
    if ( Populate_Select_Cache( Server, ISC, Mailbox, SelectCmd, SelectCmdLength ) == -1 )
    {
	return( 1 );
    }	
    
    if ( Send_Cached_Select_Response( Client, ISC, Tag ) == -1 )
    {
	snprintf( Buf, sizeof Buf - 1, "%s BAD internal proxy server error\r\n", Tag );
	IMAP_Write( Client->conn, Buf, strlen( Buf ) );
	return( 0 );
    }	
    
    return( 0 );
    
}
Esempio n. 11
0
/*++
 * Function:	Get_Server_conn
 *
 * Purpose:	When a client login attempt is made, fetch a usable server
 *              connection descriptor.  This means that either we reuse an
 *              existing ICD, or we open a new one.  Hide that abstraction from
 *              the caller...
 *
 * Parameters:	ptr to username string
 *		ptr to password string
 *              const ptr to client hostname or IP string (for logging only)
 *              in_port_t, client port number (for logging only)
 *              unsigned char - flag to indicate that the client sent the
 *                              password as a string literal.
 *
 * Returns:	ICD * on success
 *              NULL on failure
 *
 * Authors:	Dave McMurtrie <*****@*****.**>
 *
 * Credit:      Major SSL additions by Ken Murchison <*****@*****.**>
 *
 *--
 */
extern ICD_Struct *Get_Server_conn( char *Username, 
				    char *Password,
				    const char *ClientAddr,
				    in_port_t sin_port,
				    unsigned char LiteralPasswd )
{
    char *fn = "Get_Server_conn()";
    unsigned int HashIndex;
    ICC_Struct *HashEntry = NULL;
    char SendBuf[BUFSIZE];
    unsigned int BufLen = BUFSIZE - 1;
    char md5pw[MD5_DIGEST_LENGTH];
    char *tokenptr;
    char *endptr;
    char *last;
    ICC_Struct *ICC_Active;
    ICC_Struct *ICC_tptr;
    ITD_Struct Server;
    int rc;
    unsigned int Expiration;

    EVP_MD_CTX mdctx;
    int md_len;

    Expiration = PC_Struct.cache_expiration_time;
    memset( &Server, 0, sizeof Server );
    
    /* need to md5 the passwd regardless, so do that now */
    EVP_DigestInit(&mdctx, EVP_md5());
    EVP_DigestUpdate(&mdctx, Password, strlen(Password));
    EVP_DigestFinal(&mdctx, md5pw, &md_len);
    
    /* see if we have a reusable connection available */
    ICC_Active = NULL;
    HashIndex = Hash( Username, HASH_TABLE_SIZE );
    
    LockMutex( &mp );
        
    /*
     * Now we just iterate through the linked list at this hash index until
     * we either find the string we're looking for or we find a NULL.
     */
    for ( HashEntry = ICC_HashTable[ HashIndex ]; 
	  HashEntry; 
	  HashEntry = HashEntry->next )
    {
	if ( ( strcmp( Username, HashEntry->username ) == 0 ) &&
	     ( HashEntry->logouttime > 1 ) )
	{
	    ICC_Active = HashEntry;
	    /*
	     * we found this username in our hash table.  Need to know if
	     * the password matches.
	     */
	    if ( memcmp( md5pw, ICC_Active->hashedpw, sizeof md5pw ) )
	    {
		syslog( LOG_NOTICE, "%s: Unable to reuse server sd [%d] for user '%s' (%s:%d) because password doesn't match.", fn, ICC_Active->server_conn->sd, Username, ClientAddr, sin_port );
		ICC_Active->logouttime = 1;
	    }
	    else
	    {
		/*
		 * We found a matching password on an inactive server socket.
		 * We can use this guy.  Before we release the mutex, set the
		 * logouttime such that we mark this connection as "active"
		 * again.
		 */
		ICC_Active->logouttime = 0;
	
		/*
		 * The fact that we have this stored in a table as an open
		 * server socket doesn't really mean that it's open.  The
		 * server could've closed it on us.
		 * We need a speedy way to make sure this is still open.
		 * We'll set the fd to non-blocking and try to read from it.
		 * If we get a zero back, the connection is closed.  If we get
		 * EWOULDBLOCK (or some data) we know it's still open.  If we
		 * do read data, make sure we read all the data so we "drain"
		 * any puss that may be left on this socket.
		 */
		fcntl( ICC_Active->server_conn->sd, F_SETFL,
		       fcntl( ICC_Active->server_conn->sd, F_GETFL, 0) | O_NONBLOCK );
		
		while ( ( rc = IMAP_Read( ICC_Active->server_conn, Server.ReadBuf, 
				     sizeof Server.ReadBuf ) ) > 0 );
		
		if ( !rc )
		{
		    syslog(LOG_NOTICE, "%s: Unable to reuse server sd [%d] for user '%s' (%s:%d).  Connection closed by server.", fn, ICC_Active->server_conn->sd, Username, ClientAddr, sin_port );
		    ICC_Active->logouttime = 1;
		    continue;
		}
	    
		if ( errno != EWOULDBLOCK )
		{
		    syslog(LOG_NOTICE, "%s: Unable to reuse server sd [%d] for user '%s' (%s:%d). IMAP_read() error: %s", fn, ICC_Active->server_conn->sd, Username, ClientAddr, sin_port, strerror( errno ) );
		    ICC_Active->logouttime = 1;
		    continue;
		}
		
		fcntl( ICC_Active->server_conn->sd, F_SETFL, 
		       fcntl( ICC_Active->server_conn->sd, F_GETFL, 0) & ~O_NONBLOCK );
		
		/* now release the mutex and return the sd to the caller */
		UnLockMutex( &mp );

		/*
		 * We're reusing an existing server socket.  There are a few
		 * counters we have to deal with.
		 */
		IMAPCount->RetainedServerConnections--;
		IMAPCount->InUseServerConnections++;
		IMAPCount->TotalServerConnectionsReused++;
		
		if ( IMAPCount->InUseServerConnections >
		     IMAPCount->PeakInUseServerConnections )
		    IMAPCount->PeakInUseServerConnections = IMAPCount->InUseServerConnections;
	    
		syslog(LOG_INFO, "LOGIN: '******' (%s:%d) on existing sd [%d]", Username, ClientAddr, sin_port, ICC_Active->server_conn->sd );
		return( ICC_Active->server_conn );
	    }
	}
    }
    
    
    UnLockMutex( &mp );
    
    /*
     * We don't have an active connection for this user, or the password
     * didn't match.
     * Open a connection to the IMAP server so we can attempt to login 
     */
    Server.conn = ( ICD_Struct * ) malloc( sizeof ( ICD_Struct ) );
    memset( Server.conn, 0, sizeof ( ICD_Struct ) );
    Server.conn->sd = socket( PF_INET, SOCK_STREAM, IPPROTO_TCP );
    if ( Server.conn->sd == -1 )
    {
	syslog(LOG_INFO, "LOGIN: '******' (%s:%d) failed: Unable to open server socket: %s", Username, ClientAddr, sin_port, strerror( errno ) );
	goto fail;
    }

    if ( PC_Struct.send_tcp_keepalives )
    {
	int onoff = 1;
	setsockopt( Server.conn->sd, SOL_SOCKET, SO_KEEPALIVE, &onoff, sizeof onoff );
    }
    
    if ( connect( Server.conn->sd, (struct sockaddr *)&ISD.srv, 
		  sizeof(ISD.srv) ) == -1 )
    {
	syslog(LOG_INFO, "LOGIN: '******' (%s:%d) failed: Unable to connect to IMAP server: %s", Username, ClientAddr, sin_port, strerror( errno ) );
	goto fail;
    }
    
    
    /* Read & throw away the banner line from the server */
    
    if ( IMAP_Line_Read( &Server ) == -1 )
    {
	syslog(LOG_INFO, "LOGIN: '******' (%s:%d) failed: No banner line received from IMAP server", Username, ClientAddr, sin_port );
	goto fail;
    }


    /*
     * Do STARTTLS if necessary.
     */
#if HAVE_LIBSSL
    if ( PC_Struct.login_disabled )
    {
	snprintf( SendBuf, BufLen, "S0001 STARTTLS\r\n" );
	if ( IMAP_Write( Server.conn, SendBuf, strlen(SendBuf) ) == -1 )
	{
	    syslog(LOG_INFO, "STARTTLS failed: IMAP_Write() failed attempting to send STARTTLS command to IMAP server: %s", strerror( errno ) );
	    goto fail;
	}

	/*
	 * Read the server response
	 */
	if ( ( rc = IMAP_Line_Read( &Server ) ) == -1 )
	{
	    syslog(LOG_INFO, "STARTTLS failed: No response from IMAP server after sending STARTTLS command" );
	    goto fail;
	}
    
	/*
	 * Try to match up the tag in the server response to the client tag.
	 */
	endptr = Server.ReadBuf + rc;
    
	tokenptr = memtok( Server.ReadBuf, endptr, &last );
    
	if ( !tokenptr )
	{
	    /* 
	     * no tokens found in server response?  Not likely, but we still
	     * have to check.
	     */
	    syslog(LOG_INFO, "STARTTLS failed: server response to STARTTLS command contained no tokens." );
	    goto fail;
	}
    
	if ( memcmp( (const void *)tokenptr, (const void *)"S0001", 
		     strlen( tokenptr ) ) )
	{
	    /* 
	     * non-matching tag read back from the server... Lord knows what this
	     * is, so we'll fail.
	     */
	    syslog(LOG_INFO, "STARTTLS failed: server response to STARTTLS command contained non-matching tag." );
	    goto fail;
	}
    
	/*
	 * Now that we've matched the tags up, see if the response was 'OK'
	 */
	tokenptr = memtok( NULL, endptr, &last );
    
	if ( !tokenptr )
	{
	    /* again, not likely but we still have to check... */
	    syslog(LOG_INFO, "STARTTLS failed: Malformed server response to STARTTLS command" );
	    goto fail;
	}
    
	if ( memcmp( (const void *)tokenptr, "OK", 2 ) )
	{
	    /*
	     * If the server sent back a "NO" or "BAD", we can look at the actual
	     * server logs to figure out why.  We don't have to break our ass here
	     * putting the string back together just for the sake of logging.
	     */
	    syslog(LOG_INFO, "STARTTLS failed: non-OK server response to STARTTLS command" );
	    goto fail;
	}
    
	Server.conn->tls = SSL_new( tls_ctx );
	if ( Server.conn->tls == NULL )
	{
	    syslog(LOG_INFO, "STARTTLS failed: SSL_new() failed" );
	    goto fail;
	}
	    
	SSL_clear( Server.conn->tls );
	rc = SSL_set_fd( Server.conn->tls, Server.conn->sd );
	if ( rc == 0 )
	{
	    syslog(LOG_INFO, "STARTTLS failed: SSL_set_fd() failed: %d",
		   SSL_get_error( Server.conn->tls, rc ) );
	    goto fail;
	}

	SSL_set_connect_state( Server.conn->tls );
	rc = SSL_connect( Server.conn->tls );
	if ( rc <= 0 )
	{
	    syslog(LOG_INFO, "STARTTLS failed: SSL_connect() failed, %d: %s",
		   SSL_get_error( Server.conn->tls, rc ), SSLerrmessage() );
	    goto fail;
	}

	/* XXX Should we grab the session id for later reuse? */
    }
#endif /* HAVE_LIBSSL */


    /*
     * Send the login command off to the IMAP server.  Have to treat a literal
     * password different.
     */
    if ( LiteralPasswd )
    {
	snprintf( SendBuf, BufLen, "A0001 LOGIN %s {%d}\r\n", 
		  Username, strlen( Password ) );
	if ( IMAP_Write( Server.conn, SendBuf, strlen(SendBuf) ) == -1 )
	{
	    syslog(LOG_INFO, "LOGIN: '******' (%s:%d) failed: IMAP_Write() failed attempting to send LOGIN command to IMAP server: %s", Username, ClientAddr, sin_port, strerror( errno ) );
	    goto fail;
	}
	
	/*
	 * the server response should be a go ahead
	 */
	if ( ( rc = IMAP_Line_Read( &Server ) ) == -1 )
	{
	    syslog(LOG_INFO, "LOGIN: '******' (%s:%d) failed: Failed to receive go-ahead from IMAP server after sending LOGIN command", Username, ClientAddr, sin_port );
	    goto fail;
	}
	
	if ( Server.ReadBuf[0] != '+' )
	{
	    syslog( LOG_INFO, "LOGIN: '******' (%s:%d) failed: bad response from server after sending string literal specifier", Username, ClientAddr, sin_port );
	    goto fail;
	}
	
	/* 
	 * now send the password
	 */
	snprintf( SendBuf, BufLen, "%s\r\n", Password );
	
	if ( IMAP_Write( Server.conn, SendBuf, strlen( SendBuf ) ) == -1 )
	{
	    syslog(LOG_INFO, "LOGIN: '******' (%s:%d) failed: IMAP_Write() failed attempting to send literal password to IMAP server: %s", Username, ClientAddr, sin_port, strerror( errno ) );
	    goto fail;
	}
    }
    else
    {
	/*
	 * just send the login command via normal means.
	 */
	snprintf( SendBuf, BufLen, "A0001 LOGIN %s %s\r\n", 
		  Username, Password );
	
	if ( IMAP_Write( Server.conn, SendBuf, strlen(SendBuf) ) == -1 )
	{
	    syslog(LOG_INFO, "LOGIN: '******' (%s:%d) failed: IMAP_Write() failed attempting to send LOGIN command to IMAP server: %s", Username, ClientAddr, sin_port, strerror( errno ) );
	    goto fail;
	}
    }
    
	
    /*
     * Read the server response.  From RFC 3501:
     *
     * A server MAY include a CAPABILITY response code in the tagged OK
     * response to a successful LOGIN command in order to send
     * capabilities automatically.  It is unnecessary for a client to
     * send a separate CAPABILITY command if it recognizes these
     * automatic capabilities.
     *
     * We have to be ready for the possibility that this might be an 
     * untagged response...  In an ideal world, we'd want to pass the
     * untagged stuff back to the client.  For now, since the RFC doesn't
     * mandate that behaviour, we're not going to since we don't have a client
     * socket descriptor to send it to.
     */
    for ( ;; )
    {
	if ( ( rc = IMAP_Line_Read( &Server ) ) == -1 )
	{
	    syslog(LOG_INFO, "LOGIN: '******' (%s:%d) failed: No response from IMAP server after sending LOGIN command", Username, ClientAddr, sin_port );
	    goto fail;
	}
    
	if ( Server.ReadBuf[0] != '*' )
	    break;
    }
    
    
    /*
     * Try to match up the tag in the server response to the client tag.
     */
    endptr = Server.ReadBuf + rc;
    
    tokenptr = memtok( Server.ReadBuf, endptr, &last );
    
    if ( !tokenptr )
    {
	/* 
	 * no tokens found in server response?  Not likely, but we still
	 * have to check.
	 */
	syslog(LOG_INFO, "LOGIN: '******' (%s:%d) failed: server response to LOGIN command contained no tokens.", Username, ClientAddr, sin_port );
	goto fail;
    }
    
    if ( memcmp( (const void *)tokenptr, (const void *)"A0001", 
		 strlen( tokenptr ) ) )
    {
	/* 
	 * non-matching tag read back from the server... Lord knows what this
	 * is, so we'll fail.
	 */
	syslog(LOG_INFO, "LOGIN: '******' (%s:%d) failed: server response to LOGIN command contained non-matching tag.", Username, ClientAddr, sin_port );
	goto fail;
    }
    
    
    /*
     * Now that we've matched the tags up, see if the response was 'OK'
     */
    tokenptr = memtok( NULL, endptr, &last );
    
    if ( !tokenptr )
    {
	/* again, not likely but we still have to check... */
	syslog(LOG_INFO, "LOGIN: '******' (%s:%d) failed: Malformed server response to LOGIN command", Username, ClientAddr, sin_port );
	goto fail;
    }
    
    if ( memcmp( (const void *)tokenptr, "OK", 2 ) )
    {
	/*
	 * If the server sent back a "NO" or "BAD", we can look at the actual
	 * server logs to figure out why.  We don't have to break our ass here
	 * putting the string back together just for the sake of logging.
	 */
	syslog(LOG_INFO, "LOGIN: '******' (%s:%d) failed: non-OK server response to LOGIN command", Username, ClientAddr, sin_port );
	goto fail;
    }
    
    /*
     * put this in our used list and remove it from the free list
     */
    for( ; ; )
    {
	LockMutex( &mp );
	
	if ( ICC_free->next )
	{
	    /* generate the hash index */
	    HashIndex = Hash( Username, HASH_TABLE_SIZE );
	    
	    /* temporarily store the address of the next free structure */
	    ICC_tptr = ICC_free->next;
	    
	    /*
	     * We want to add the newest "used" structure at the front of
	     * the list at the hash index.
	     */
	    ICC_free->next = ICC_HashTable[ HashIndex ];
	    ICC_HashTable[ HashIndex ] = ICC_free;
	    
	    /* 
	     * less typing and more readability, set an "active" pointer.
	     */
	    ICC_Active = ICC_free;
	    
	    /* now point the free listhead to the next available free struct */
	    ICC_free = ICC_tptr;
	    
	    /* fill in the newest used (oxymoron?) structure */
	    strncpy( ICC_Active->username, Username, 
		     sizeof ICC_Active->username );
	    memcpy( ICC_Active->hashedpw, md5pw, sizeof ICC_Active->hashedpw );
	    ICC_Active->logouttime = 0;    /* zero means, "it's active". */
	    ICC_Active->server_conn = Server.conn;
	    
	    UnLockMutex( &mp );
	    
	    IMAPCount->InUseServerConnections++;
	    IMAPCount->TotalServerConnectionsCreated++;

	    if ( IMAPCount->InUseServerConnections >
		 IMAPCount->PeakInUseServerConnections )
		IMAPCount->PeakInUseServerConnections = IMAPCount->InUseServerConnections;
	    syslog(LOG_INFO, "LOGIN: '******' (%s:%d) on new sd [%d]", Username, ClientAddr, sin_port, Server.conn->sd );
	    return( Server.conn );
	}
	
	/*
	 * There weren't any free ICC structs.  Try to free one.  Make sure
	 * we unlock the mutex, since ICC_Recycle needs to obtain it.
	 */
	UnLockMutex( &mp );
	
	Expiration = abs( Expiration / 2 );
	
	/*
	 * Eventually, we have to fail
	 */
	if ( Expiration <= 2 )
	{
	    syslog(LOG_INFO, "LOGIN: '******' (%s:%d) failed: Out of free ICC structs.", Username, ClientAddr, sin_port );
	    goto fail;
	}
	
	ICC_Recycle( Expiration );
	
    }
    
  fail:
#if HAVE_LIBSSL
    if ( Server.conn->tls )
    {
	SSL_shutdown( Server.conn->tls );
	SSL_free( Server.conn->tls );
    }
#endif
    close( Server.conn->sd );
    free( Server.conn );
    return( NULL );
}
Esempio n. 12
0
/*++
 * 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:	nuttin -- exits if there's a problem
 *
 * Authors:	Dave McMurtrie <*****@*****.**>
 *
 * 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 void SetBannerAndCapability( void )
{
    int sd;
    ITD_Struct itd;
    ICD_Struct conn;
    int BytesRead;
    char *fn = "SetBannerAndCapability()";
    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 -- exiting", fn,
                   strerror(errno) );
            exit( 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 );    /* IMAP server may not be started yet. */
                continue;
            }
            syslog( LOG_ERR, "%s: unable to connect() to imap server and retry limit exceeded -- exiting.", fn );
            exit( 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 )
    {
        syslog( LOG_ERR, "%s: Error reading banner line from server on initial connection: %s -- Exiting.", fn, strerror( errno ) );
        close( itd.conn->sd );
        exit( 1 );
    }

    if ( itd.LiteralBytesRemaining )
    {
        syslog( LOG_ERR, "%s: Server sent unexpected literal specifier in banner response -- Exiting.", fn );
        exit( 1 );
    }


    BannerLen = ParseBannerAndCapability( Banner, sizeof Banner - 1,
                                          itd.ReadBuf, 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 -- Exiting.", fn, Banner);
        close( itd.conn->sd );
        exit( 1 );
    }


    /* Now we send a CAPABILITY command to the server. */
    if ( IMAP_Write( itd.conn, "1 CAPABILITY\r\n", strlen("1 CAPABILITY\r\n") ) == -1 )
    {
        syslog(LOG_ERR, "%s: Unable to send capability command to server: %s -- exiting.", fn, strerror(errno) );
        close( itd.conn->sd );
        exit( 1 );
    }

    /*
     * From RFC2060:
     * The server MUST send a single untagged
     * CAPABILITY response with "IMAP4rev1" as one of the listed
     * capabilities before the (tagged) OK response.
     *
     * The means we should read exactly 2 lines of data back from the server.
     * The first will be the untagged capability line.
     * The second will be the OK response with the tag in it.
     */

    BytesRead = IMAP_Line_Read( &itd );
    if ( BytesRead == -1 )
    {
        syslog( LOG_ERR, "%s: Failed to read capability response from server: %s --  exiting.", fn, strerror( errno ) );
        close( itd.conn->sd );
        exit( 1 );
    }

    if ( itd.LiteralBytesRemaining )
    {
        syslog( LOG_ERR, "%s: Server sent unexpected literal specifier in CAPABILITY response -- Exiting.", fn );
        close( itd.conn->sd );
        exit ( 1 );

    }

    CapabilityLen = ParseBannerAndCapability( Capability, sizeof Capability - 1,
                    itd.ReadBuf, BytesRead );


    /* Now read the tagged response and make sure it's OK */
    BytesRead = IMAP_Line_Read( &itd );
    if ( BytesRead == -1 )
    {
        syslog( LOG_ERR, "%s: Failed to read capability response from server: %s -- exiting.", fn, strerror( errno ) );
        close( itd.conn->sd );
        exit( 1 );
    }

    if ( itd.LiteralBytesRemaining )
    {
        syslog( LOG_ERR, "%s: Server sent unexpected literal specifier in tagged CAPABILITY response -- exiting.", fn );
        exit( 1 );
    }

    if ( strncasecmp( itd.ReadBuf, IMAP_TAGGED_OK, strlen(IMAP_TAGGED_OK) ) )
    {
        syslog(LOG_ERR, "%s: Received non-OK tagged reponse from imap server on CAPABILITY command -- exiting.", fn );
        close( itd.conn->sd );
        exit( 1 );
    }

    /* Be nice and logout */
    if ( IMAP_Write( itd.conn, "2 LOGOUT\r\n", strlen("2 LOGOUT\r\n") ) == -1 )
    {
        syslog(LOG_WARNING, "%s: IMAP_Write() failed on LOGOUT: %s -- Ignoring", fn, strerror(errno) );
        close( itd.conn->sd );
        return;
    }

    /* read the final OK logout */
    BytesRead = IMAP_Line_Read( &itd );
    if ( BytesRead == -1 )
    {
        syslog(LOG_WARNING, "%s: IMAP_Line_Read() failed on LOGOUT -- Ignoring", fn );
    }

    if ( itd.LiteralBytesRemaining )
    {
        syslog( LOG_WARNING, "%s: Server sent unexpected literal specifier in LOGOUT response -- Ignoring", fn );
    }

    close( itd.conn->sd );
    return;
}
Esempio n. 13
0
/*++
 * Function:	_ICC_Recycle
 *
 * Purpose:	core logic to implement the ICC_Recycle() & ICC_Recycle_Loop()
 *              functions.
 *
 * Parameters:	unsigned int -- ICC expiration time
 *
 * Returns:	nada
 *	
 * Authors:	Dave McMurtrie <*****@*****.**>
 *--
 */
static void _ICC_Recycle( unsigned int Expiration )
{
    char *fn = "_ICC_Recycle()";
    time_t CurrentTime;
    int rc;
    unsigned int HashIndex;
    ICC_Struct *HashEntry;
    ICC_Struct *Previous;
    
    CurrentTime = time(0);

    LockMutex( &mp );
    
    /*
     * Need to iterate through every single item in our hash table
     * to decide if we can free it or not.
     */
    for ( HashIndex = 0; HashIndex < HASH_TABLE_SIZE; HashIndex++ )
    {
	
	Previous = NULL;
	HashEntry = ICC_HashTable[ HashIndex ];
	
	while ( HashEntry )
	{
	    /*
	     * If the last logout time is non-zero, and it's been logged 
	     * out for longer than our default expiration time, free it.
	     * Note that this allows for the logouttime to be explicitly
	     * set to 1 (such as in the Get_Server_conn code) if we want to
	     * reap a connection before waiting the normal expiration
	     * cycle.
	     */
	    if ( HashEntry->logouttime &&
		 ( ( CurrentTime - HashEntry->logouttime ) > 
		   Expiration ) )
	    {
		syslog(LOG_INFO, "Expiring server sd [%d]", HashEntry->server_conn->sd);
		/* Logout of the imap server and close the server socket. */

		IMAP_Write( HashEntry->server_conn, "VIC20 LOGOUT\r\n",
			    strlen( "VIC20 LOGOUT\r\n" ) );

#if HAVE_LIBSSL
		if ( HashEntry->server_conn->tls )
		{
		    SSL_shutdown( HashEntry->server_conn->tls );
		    SSL_free( HashEntry->server_conn->tls );
		}
#endif
		close( HashEntry->server_conn->sd );
		free( HashEntry->server_conn );
		
		/*
		 * This was being counted as a "retained" connection.  It was
		 * open, but not in use.  Now that we're closing it, we have
		 * to decrement the number of retained connections.
		 */
		IMAPCount->RetainedServerConnections--;
		

		if ( Previous )
		{
		    Previous->next = HashEntry->next;
		    HashEntry->next = ICC_free;
		    ICC_free = HashEntry;
		    HashEntry = Previous->next;
		}
		else
		{
		    ICC_HashTable[ HashIndex ] = HashEntry->next;
		    HashEntry->next = ICC_free;
		    ICC_free = HashEntry;
		    HashEntry = ICC_HashTable[ HashIndex ];
		}
	    }
	    else
	    {
		Previous = HashEntry;
		HashEntry = HashEntry->next;
	    }
	}
    }
    
    UnLockMutex( &mp );
}
Esempio n. 14
0
/*++
 * 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 <*****@*****.**>
 *
 * 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;
    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 );    /* IMAP 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 IMAP server would cause buffer overflow.", fn, BytesRead );
	close( itd.conn->sd );
	return( -1 );
    }
    
    memcpy( Banner, itd.ReadBuf, 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 );
    }


    /* Now we send a CAPABILITY command to the server. */
    if ( IMAP_Write( itd.conn, "1 CAPABILITY\r\n", strlen("1 CAPABILITY\r\n") ) == -1 )
    {
	syslog(LOG_ERR, "%s: IMAP_Write() failed: %s", fn, strerror(errno) );
	close( itd.conn->sd );
	return( -1 );
    }
    
    /*
     * From RFC2060:
     * The server MUST send a single untagged
     * CAPABILITY response with "IMAP4rev1" as one of the listed
     * capabilities before the (tagged) OK response.
     *
     * The means we should read exactly 2 lines of data back from the server.
     * The first will be the untagged capability line.
     * The second will be the OK response with the tag in it.
     */

    BytesRead = IMAP_Line_Read( &itd );
    if ( BytesRead == -1 )
    {
	close( itd.conn->sd );
	return( -1 );
    }
    
    /*
     * The read buffer should now contain the 
     * untagged response line.  
     */
    if ( sizeof Capability < BytesRead )
    {
	syslog(LOG_ERR, "%s: Storing %d byte capability string from IMAP server would cause buffer overflow.", fn, BytesRead );
	close( itd.conn->sd );
	return( -1 );
    }

    /*
     * strip out all of the AUTH mechanisms except the ones that we support.
     * Right now, this is just AUTH=LOGIN.  Note that the use of
     * non-MT safe strtok is okay here.  This function is called before any
     * other threads are launched and should never be called again.
     */
    itd.ReadBuf[BytesRead - 2] = '\0';
    CP = strtok( itd.ReadBuf, " " );
    
    if ( !CP )
    {
	syslog( LOG_ERR, "%s: No tokens found in capability string sent from IMAP server.", fn);
	close( itd.conn->sd );
	return( -1 );
    }
    
    sprintf( Capability, CP );
    
    /*
     * initially assume that the server doesn't support UNSELECT.
     */
    PC_Struct.support_unselect = UNSELECT_NOT_SUPPORTED;

    /*
     * initially assume that the server doesn't support STARTTLS.
     */
    PC_Struct.support_starttls = STARTTLS_NOT_SUPPORTED;

    /*
     * initially assume that the server doesn't disable LOGIN.
     */
    PC_Struct.login_disabled = LOGIN_NOT_DISABLED;

    for( ; ; )
    {
	CP = strtok( NULL, " " );
	
	if ( !CP )
	    break;

	if ( !strncasecmp( CP, "UNSELECT", strlen( "UNSELECT" ) ) )
	{
	    PC_Struct.support_unselect = UNSELECT_SUPPORTED;
	}
	
	/*
	 * If this token happens to be an auth mechanism, we want to
	 * discard it unless it's an auth mechanism we can support.
	 */
	if ( ! strncasecmp( CP, "AUTH=", strlen( "AUTH=" ) ) &&
	     ( strncasecmp( CP, "AUTH=LOGIN", strlen( "AUTH=LOGIN" ) ) ) )
	{
	    continue;
	}
	
	/*
	 * If this token happens to be SASL, we want to discard it
	 * since we don't support any auth mechs that can use it.
	 */
	if ( !strncasecmp( CP, "SASL", strlen( "SASL" ) ) )
	{
	    continue;
	}

	/*
	 * If this token happens to be STARTTLS, we want to discard it
	 * since we don't support it on the client-side.
	 */
	if ( ! strncasecmp( CP, "STARTTLS", strlen( "STARTTLS" ) ) )
	{
	    PC_Struct.support_starttls = STARTTLS_SUPPORTED;
	    continue;
	}
	
	/*
	 * If this token happens to be LOGINDISABLED, we want to discard it
	 * since we don't support it on the client-side.
	 */
	if ( ! strncasecmp( CP, "LOGINDISABLED", strlen( "LOGINDISABLED" ) ) )
	{
	    PC_Struct.login_disabled = LOGIN_DISABLED;
	    continue;
	}
	
	strcat( Capability, " ");
	strcat( Capability, CP );
    }
    
    strcat( Capability, "\r\n" );
    
    CapabilityLen = strlen( Capability );
    
    /* Now read the tagged response and make sure it's OK */
    BytesRead = IMAP_Line_Read( &itd );
    if ( BytesRead == -1 )
    {
	close( itd.conn->sd );
	return( -1 );
    }
    
    
    if ( strncasecmp( itd.ReadBuf, IMAP_TAGGED_OK, strlen(IMAP_TAGGED_OK) ) )
    {
	syslog(LOG_ERR, "%s: Received non-OK tagged reponse from imap server on CAPABILITY command", fn );
	close( itd.conn->sd );
	return( -1 );
    }
    
    /* Be nice and logout */
    if ( IMAP_Write( itd.conn, "2 LOGOUT\r\n", strlen("2 LOGOUT\r\n") ) == -1 )
    {
	syslog(LOG_WARNING, "%s: IMAP_Write() failed on LOGOUT: %s -- Returning success anyway.", fn, strerror(errno) );
	close( itd.conn->sd );
	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 );
}
Esempio n. 15
0
/*++
 * 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:	nuttin -- exits if there's a problem
 *
 * Authors:	Dave McMurtrie <*****@*****.**>
 *
 * 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 void SetBannerAndCapability( void )
{
    int sd;
    ITD_Struct itd;
    ICD_Struct conn;
    int BytesRead;
    char *fn = "SetBannerAndCapability()";

    /* initialize some stuff */
    memset( &itd, 0, sizeof itd );

    for ( ;; )
    {
	sd = socket( ISD.srv->ai_family, ISD.srv->ai_socktype,
		     ISD.srv->ai_protocol );
	if ( sd == -1 )
	{
	    syslog(LOG_ERR, "%s: socket() failed: %s -- exiting", fn, 
		   strerror(errno) );
	    exit( 1 );
	}
	
	if ( connect( sd, (struct sockaddr *)ISD.srv->ai_addr, 
		      ISD.srv->ai_addrlen ) == -1 ) 	{
	    syslog(LOG_ERR, "%s: connect() to IMAP server on socket [%d] failed: %s -- retrying", fn, sd, strerror(errno));
	    close( sd );
	    
	    sleep( 15 );    /* IMAP server may not be started yet. */
	}
	else
	{
	    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 )
    {
	syslog( LOG_ERR, "%s: Error reading banner line from server on initial connection: %s -- Exiting.", fn, strerror( errno ) );
	close( itd.conn->sd );
	exit( 1 );
    }

    if ( itd.LiteralBytesRemaining )
    {
	syslog( LOG_ERR, "%s: Server sent unexpected literal specifier in banner response -- Exiting.", fn );
	exit( 1 );
    }
    

    BannerLen = ParseBannerAndCapability( Banner, sizeof Banner - 1,
					  itd.ReadBuf, BytesRead, 0 );
    
    /*
     * 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 -- Exiting.", fn, Banner);
	close( itd.conn->sd );
	exit( 1 );
    }


    /* Now we send a CAPABILITY command to the server. */
    if ( IMAP_Write( itd.conn, "1 CAPABILITY\r\n", strlen("1 CAPABILITY\r\n") ) == -1 )
    {
	syslog(LOG_ERR, "%s: Unable to send capability command to server: %s -- exiting.", fn, strerror(errno) );
	close( itd.conn->sd );
	exit( 1 );
    }
    
    /*
     * From RFC2060:
     * The server MUST send a single untagged
     * CAPABILITY response with "IMAP4rev1" as one of the listed
     * capabilities before the (tagged) OK response.
     *
     * The means we should read exactly 2 lines of data back from the server.
     * The first will be the untagged capability line.
     * The second will be the OK response with the tag in it.
     */

    BytesRead = IMAP_Line_Read( &itd );
    if ( BytesRead == -1 )
    {
	syslog( LOG_ERR, "%s: Failed to read capability response from server: %s --  exiting.", fn, strerror( errno ) );
	close( itd.conn->sd );
	exit( 1 );
    }

    if ( itd.LiteralBytesRemaining )
    {
	syslog( LOG_ERR, "%s: Server sent unexpected literal specifier in CAPABILITY response -- Exiting.", fn );
	close( itd.conn->sd );
	exit ( 1 );
	
    }
    
    CapabilityLen = ParseBannerAndCapability( Capability, sizeof Capability - 1,
					      itd.ReadBuf, BytesRead, 1 );
    
    /* Now read the tagged response and make sure it's OK */
    BytesRead = IMAP_Line_Read( &itd );
    if ( BytesRead == -1 )
    {
	syslog( LOG_ERR, "%s: Failed to read capability response from server: %s -- exiting.", fn, strerror( errno ) );
	close( itd.conn->sd );
	exit( 1 );
    }

    if ( itd.LiteralBytesRemaining )
    {
	syslog( LOG_ERR, "%s: Server sent unexpected literal specifier in tagged CAPABILITY response -- exiting.", fn );
	exit( 1 );
    }
    
    if ( strncasecmp( itd.ReadBuf, IMAP_TAGGED_OK, strlen(IMAP_TAGGED_OK) ) )
    {
	syslog(LOG_ERR, "%s: Received non-OK tagged reponse from IMAP server on CAPABILITY command -- exiting.", fn );
	close( itd.conn->sd );
	exit( 1 );
    }
    
    /*
     * If we're using it, attempt STARTTLS before doing one more
     * CAPABILITY so our capability string is accurate
     */
    if ( PC_Struct.login_disabled || PC_Struct.force_tls )
    {
#if HAVE_LIBSSL
	if ( PC_Struct.support_starttls != STARTTLS_NOT_SUPPORTED )
	{
	    if ( Attempt_STARTTLS( &itd ) != 0 )
	    {
		syslog(LOG_ERR, "%s: STARTTLS failed for CAPABILITY check -- exiting.", fn );
		close( itd.conn->sd );
		exit( 1 );
	    }
	    else
	    {
		/*
		 * STARTTLS was successful, so we can proceed
		 * to get the new CAPABILITY list - first,
		 * send a CAPABILITY command to the server.
		 */
		if ( IMAP_Write( itd.conn, "1 CAPABILITY\r\n", strlen("1 CAPABILITY\r\n") ) == -1 )
		{
		    syslog(LOG_ERR, "%s: Unable to send capability command to server: %s -- exiting.", fn, strerror(errno) );
		    close( itd.conn->sd );
		    exit( 1 );
		}

		/*
		 * From RFC2060:
		 * The server MUST send a single untagged
		 * CAPABILITY response with "IMAP4rev1" as one of the listed
		 * capabilities before the (tagged) OK response.
		 *
		 * The means we should read exactly 2 lines of data back from the server.
		 * The first will be the untagged capability line.
		 * The second will be the OK response with the tag in it.
		 */

		BytesRead = IMAP_Line_Read( &itd );
		if ( BytesRead == -1 )
		{
		    syslog( LOG_ERR, "%s: Failed to read capability response from server: %s --  exiting.", fn, strerror( errno ) );
		    close( itd.conn->sd );
		    exit( 1 );
		}

		if ( itd.LiteralBytesRemaining )
		{
		    syslog( LOG_ERR, "%s: Server sent unexpected literal specifier in CAPABILITY response -- Exiting.", fn );
		    close( itd.conn->sd );
		    exit ( 1 );
		}

		CapabilityLen = ParseBannerAndCapability( Capability, sizeof Capability - 1,
		itd.ReadBuf, BytesRead, 1 );

		/* Now read the tagged response and make sure it's OK */
		BytesRead = IMAP_Line_Read( &itd );
		if ( BytesRead == -1 )
		{
		    syslog( LOG_ERR, "%s: Failed to read capability response from server: %s -- exiting.", fn, strerror( errno ) );
		    close( itd.conn->sd );
		    exit( 1 );
		}

		if ( itd.LiteralBytesRemaining )
		{
		    syslog( LOG_ERR, "%s: Server sent unexpected literal specifier in tagged CAPABILITY response -- exiting.", fn );
		    exit( 1 );
		}

		if ( strncasecmp( itd.ReadBuf, IMAP_TAGGED_OK, strlen(IMAP_TAGGED_OK) ) )
		{
		    syslog(LOG_ERR, "%s: Received non-OK tagged reponse from imap server on CAPABILITY command -- exiting.", fn );
		    close( itd.conn->sd );
		    exit( 1 );
		}
	    }
	}
	else
#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);
	    close( itd.conn->sd );
	    exit( 1 );
	}
    }
    else
    {
	//syslog( LOG_ERR, "%s: Not trying STARTTLS and second CAPABILITY.", fn );
    }

    /* Be nice and logout */
    if ( IMAP_Write( itd.conn, "2 LOGOUT\r\n", strlen("2 LOGOUT\r\n") ) == -1 )
    {
	syslog(LOG_WARNING, "%s: IMAP_Write() failed on LOGOUT: %s -- Ignoring", fn, strerror(errno) );
	close( itd.conn->sd );
	return;
    }
    
    /* read the final OK logout */
    BytesRead = IMAP_Line_Read( &itd );
    if ( BytesRead == -1 )
    {
	syslog(LOG_WARNING, "%s: IMAP_Line_Read() failed on LOGOUT -- Ignoring", fn );
    }

    if ( itd.LiteralBytesRemaining )
    {
	syslog( LOG_WARNING, "%s: Server sent unexpected literal specifier in LOGOUT response -- Ignoring", fn );
    }
        
    close( itd.conn->sd );
    return;
}