Logo Search packages:      
Sourcecode: medusa version File versions  Download package

smtp-auth.c

/*
**   SMTP-AUTH Authentication Daemon Password Checking Medusa Module
**
**   ------------------------------------------------------------------------
**    Copyright (C) 2006 pMonkey
**    pMonkey <pmonkey@foofus.net>
**
**    This program is free software; you can redistribute it and/or modify
**    it under the terms of the GNU General Public License version 2,
**    as published by the Free Software Foundation
**
**    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.
**
**    http://www.gnu.org/licenses/gpl.txt
**
**    This program is released under the GPL with the additional exemption
**    that compiling, linking, and/or using OpenSSL is allowed.
**
**   ------------------------------------------------------------------------
**
**
*/

#include <sys/types.h>
#include <libgen.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include "module.h"

#define MODULE_NAME    "smtp-auth.mod"
#define MODULE_AUTHOR  "pmonkey@foofus.net"
#define MODULE_SUMMARY_USAGE  "Brute force module for SMTP Authentication with TLS"
#define MODULE_VERSION    "0.9.1"
#define MODULE_VERSION_SVN "$Id: smtp-auth.c 694 2007-03-29 19:59:22Z pmonkey $"
#define MODULE_SUMMARY_FORMAT  "%s : version %s"
#define MODULE_SUMMARY_FORMAT_WARN  "%s : version %s (%s)"

#define FREE(x) \
        if (x != NULL) { \
           free(x); \
           x = NULL; \
        }

#define PORT_SMTPAUTH 25
#define BUF_SIZE 300

typedef struct __SMTPAUTH_DATA {
  int iMode;
} _SMTPAUTH_DATA;


// Tells us whether we are to continue processing or not
enum MODULE_STATE
{
  MSTATE_NEW,
  MSTATE_RUNNING,
  MSTATE_EXITING
};

// Forward declarations
int getAuthType(int hSocket, _SMTPAUTH_DATA* _psSessionData);
int initConnection(int hSocket, sConnectParams *params);
int tryLogin(int hSocket, sLogin** login, _SMTPAUTH_DATA* _psSessionData, char* szLogin, char* szPassword);
int initModule(sLogin* login, _SMTPAUTH_DATA *_psSessionData);

// Tell medusa how many parameters this module allows
int getParamNumber()
{
  return 0;    // we don't need no stinking parameters
}

// Displays information about the module and how it must be used
void summaryUsage(char **ppszSummary)
{
  // Memory for ppszSummary will be allocated here - caller is responsible for freeing it
  int  iLength = 0;

  if (*ppszSummary == NULL)
  {
    iLength = strlen(MODULE_SUMMARY_USAGE) + strlen(MODULE_VERSION) + strlen(MODULE_SUMMARY_FORMAT) + 1;
    *ppszSummary = (char*)malloc(iLength);
    memset(*ppszSummary, 0, iLength);
    snprintf(*ppszSummary, iLength, MODULE_SUMMARY_FORMAT, MODULE_SUMMARY_USAGE, MODULE_VERSION);
  } 
  else 
  {
    writeError(ERR_ERROR, "%s reports an error in summaryUsage() : ppszSummary must be NULL when called", MODULE_NAME);
  }

}

/* Display module usage information */
void showUsage()
{
  writeVerbose(VB_NONE, "%s (%s) %s :: %s\n", MODULE_NAME, MODULE_VERSION, MODULE_AUTHOR, MODULE_SUMMARY_USAGE);
  writeVerbose(VB_NONE, "");
  writeVerbose(VB_NONE, "");
}

/* Stolen Right Out of Mutt */
void mutt_to_base64 (unsigned char *out, unsigned char *in, size_t len, size_t olen)
{
  int j;

  char B64Chars[64] = {
    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
    'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
    'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
    't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
    '8', '9', '+', '/'
  };

  /* Back to Mutt code */
  while (len >= 3 && olen > 10)
  {
    *out++ = B64Chars[in[0] >> 2];
    *out++ = B64Chars[((in[0] << 4) & 0x30) | (in[1] >> 4)];
    *out++ = B64Chars[((in[1] << 2) & 0x3c) | (in[2] >> 6)];
    *out++ = B64Chars[in[2] & 0x3f];
    olen  -= 4;
    len   -= 3;
    in    += 3;
  }

  /* clean up remainder */
  if (len > 0 && olen > 4)
  {
    unsigned char fragment;

    *out++ = B64Chars[in[0] >> 2];
    fragment = (in[0] << 4) & 0x30;
    if (len > 1)
      fragment |= in[1] >> 4;
    *out++ = B64Chars[fragment];
    *out++ = (len < 2) ? '=' : B64Chars[(in[1] << 2) & 0x3c];
    *out++ = '=';
  }
  *out = '\0';
}

// The "main" of the medusa module world - this is what gets called to actually do the work
int go(sLogin* logins, int argc, char *argv[])
{
  int i;
  char *strtok_ptr = NULL, *pOpt = NULL, *pOptTmp = NULL;
  _SMTPAUTH_DATA *psSessionData = NULL;
  psSessionData = malloc(sizeof(_SMTPAUTH_DATA));
  memset(psSessionData, 0, sizeof(_SMTPAUTH_DATA));

  if ( !(0 <= argc <= 3) )
  {
    // Show usage information
    writeError(ERR_ERROR, "%s is expecting 0 parameters, but it was passed %d", MODULE_NAME, argc);
  } 
  else 
  {
    writeError(ERR_DEBUG_MODULE, "OMG teh %s module has been called!!", MODULE_NAME);

    initModule(logins, psSessionData);
  }  

  return 0;
}

int initModule(sLogin* psLogin, _SMTPAUTH_DATA *_psSessionData)
{
  int hSocket = -1;
  enum MODULE_STATE nState = MSTATE_NEW;
  char* bufReceive = NULL;
  int nReceiveBufferSize = 0, nFirstPass = 0, nFoundPrompt = 0;
  int i = 0;
  char *pPass = NULL;
  sUser* user = psLogin->psUser;
  sConnectParams params;

  memset(&params, 0, sizeof(sConnectParams));

  if (psLogin->psServer->psAudit->iPortOverride > 0)
    params.nPort = psLogin->psServer->psAudit->iPortOverride;
  else
    params.nPort = PORT_SMTPAUTH;

  params.nSSLVersion = 9; /* Force use of TLS */
  initConnectionParams(psLogin, &params);

  if (user != NULL) 
  {
    writeError(ERR_DEBUG_MODULE, "[%s] module started for host: %s user: '%s'", MODULE_NAME, psLogin->psServer->pHostIP, user->pUser);
  }
  else 
  {
    writeError(ERR_DEBUG_MODULE, "[%s] module started for host: %s", MODULE_NAME, psLogin->psServer->pHostIP);
  }

  pPass = getNextPass(psLogin->psServer->psAudit, user);
  if (pPass == NULL)
  {
    writeVerbose(VB_GENERAL, "[%s] out of passwords for user '%s' at host '%s', bailing", MODULE_NAME, user->pUser, psLogin->psServer->pHostIP);
  }

  while(NULL != pPass)
  {  
    switch(nState)
    {
      case MSTATE_NEW:
        if (hSocket > 0)
          medusaDisconnect(hSocket);
  
        hSocket = medusaConnect(&params);
        if (hSocket < 0) 
        {
          writeError(ERR_NOTICE, "[%s] failed to connect, port %d was not open on %s", MODULE_NAME, params.nPort, psLogin->psServer->pHostIP);
          psLogin->iResult = LOGIN_RESULT_UNKNOWN;
          psLogin->iStatus = LOGIN_FAILED;
          FREE(_psSessionData);
          return FAILURE;
        }

        if (initConnection(hSocket, &params) == FAILURE)
        {
          psLogin->iResult = LOGIN_RESULT_UNKNOWN;
          psLogin->iStatus = LOGIN_FAILED;
          FREE(_psSessionData);
          return FAILURE;
        }

        writeError(ERR_DEBUG_MODULE, "Connected");

        nState = MSTATE_RUNNING;
        break;
      case MSTATE_RUNNING:
        nState = tryLogin(hSocket, &psLogin, _psSessionData, user->pUser, pPass);
        if (psLogin->iResult != LOGIN_RESULT_UNKNOWN)
          pPass = getNextPass(psLogin->psServer->psAudit, user);
        break;
      case MSTATE_EXITING:
        if (hSocket > 0)
          medusaDisconnect(hSocket);
        hSocket = -1;
        pPass = NULL;
        break;
      default:
        writeError(ERR_CRITICAL, "Unknown %s module state %d", MODULE_NAME, nState);
        if (hSocket > 0)
          medusaDisconnect(hSocket);
        hSocket = -1;
        psLogin->iResult = LOGIN_RESULT_UNKNOWN;
        psLogin->iStatus = LOGIN_FAILED;
        FREE(_psSessionData);
        return FAILURE;
    }  
  }

  psLogin->iStatus = LOGIN_DONE;
  FREE(_psSessionData);
  return SUCCESS;
}
       
/* Module specific functions */

#define AUTH_UNKNOWN 0
#define AUTH_PLAIN 1
#define AUTH_LOGIN 2
#define AUTH_NTLM 3


void markCloseSocket(int sig)
{
        writeError(ERR_ERROR, "[%s] The socket closed dude. Where's my beer?",MODULE_NAME);
}


int initConnection(int hSocket, sConnectParams *params)
{ 
  int iRet;
  unsigned char bufSend[BUF_SIZE];
  unsigned char* bufReceive = NULL;
  int nReceiveBufferSize;
  int nRes;
  struct sigaction act;
  act.sa_handler        = markCloseSocket; 
  sigemptyset(&act.sa_mask);
  act.sa_flags  = 0;
  
  nRes  = sigaction(SIGPIPE,&act,0);
  if (!nRes)
    {
      /* Proceed as planned */
    } else {
      printf("Umm...no signal handlers??? WTF?\n");
      exit(99);
    }




  nReceiveBufferSize = 0;
  bufReceive = medusaReceiveRaw(hSocket, &nReceiveBufferSize);
  if (bufReceive == NULL)
  {
    writeError(ERR_ERROR, "[%s] failed: medusaReceive returned no data.", MODULE_NAME);
    return FAILURE;
  }
  else if (strncmp(bufReceive, "220", 3) != 0)
  {
    writeError(ERR_ERROR, "[%s] Is this an SMTP server?: %s.", MODULE_NAME, bufReceive);
    FREE(bufReceive);  
    return FAILURE;
  }
  FREE(bufReceive);
 
  memset(bufSend, 0, BUF_SIZE);
  sprintf(bufSend, "EHLO gerg\r\n");
  if (medusaSend(hSocket, bufSend, strlen(bufSend), 0) < 0)
  {
    writeError(ERR_ERROR, "[%s] failed: medusaSend was not successful", MODULE_NAME);
    return FAILURE;
  }
  
  nReceiveBufferSize = 0;
  bufReceive = NULL;
  bufReceive = medusaReceiveRaw(hSocket, &nReceiveBufferSize);
  if (bufReceive == NULL)
  {
    writeError(ERR_ERROR, "[%s] failed: medusaReceive returned no data.", MODULE_NAME);
    return FAILURE;
  }

  if (strstr(bufReceive, "250-AUTH") != NULL)
  {
    /* Mode AUTH without SSL protection crikey */
    writeError(ERR_DEBUG_MODULE, "[%s] AUTH seen without TLS", MODULE_NAME);
  }

  if (strstr(bufReceive, "250-STARTTLS") != NULL)
  {
    FREE(bufReceive);
    
    memset(bufSend, 0, BUF_SIZE);
    sprintf(bufSend, "STARTTLS\r\n");
    if (medusaSend(hSocket, bufSend, strlen(bufSend), 0) < 0)
    {
      writeError(ERR_ERROR, "[%s] failed: medusaSend was not successful", MODULE_NAME);
      return FAILURE;
    }
  
    nReceiveBufferSize = 0;
    bufReceive = medusaReceiveRaw(hSocket, &nReceiveBufferSize);
    if (bufReceive == NULL)
    {
      writeError(ERR_ERROR, "[%s] failed: medusaReceive returned no data.", MODULE_NAME);
      return FAILURE;
    }
    else if (strstr(bufReceive, "220") != NULL)
    {
      FREE(bufReceive);
      
      if (medusaConnectSocketSSL(params, hSocket) < 0)
      {
        writeError(ERR_ERROR, "[%s] Failed to establish SSLv3 connection.", MODULE_NAME);
        return FAILURE;
      }
    }
  }
  else
  {
    FREE(bufReceive);
  }  

  return SUCCESS;
}

/* Module Specific Functions */

int getAuthType(int hSocket, _SMTPAUTH_DATA* _psSessionData)
{
  int iRet;
  char* bufReceive;
  unsigned char* bufSend;
  int nSendBufferSize = 0;
  int nReceiveBufferSize = 0;

  if (medusaSend(hSocket, bufSend, strlen(bufSend), 0) < 0)
  {
    writeError(ERR_ERROR, "%s failed: medusaSend was not successful", MODULE_NAME);
    return FAILURE;
  }

  bufReceive = medusaReceiveRaw(hSocket, &nReceiveBufferSize);
  if (bufReceive == NULL)
  {
    writeError(ERR_ERROR, "%s failed: medusaReceive returned no data. Exiting...", MODULE_NAME);
    return FAILURE;
  }
  else if ((strstr(bufReceive,"AUTH=LOGIN") != NULL))
  {
    writeError(ERR_DEBUG_MODULE, "Detected authentication type: LOGIN");
    _psSessionData->iMode = AUTH_LOGIN;
  }
  else if ((strstr(bufReceive,"AUTH=PLAIN") != NULL))
  {
    writeError(ERR_DEBUG_MODULE, "Detected authentication type: PLAIN");
    _psSessionData->iMode = AUTH_PLAIN;
  }
  else
  {
    writeError(ERR_ERROR, "%s failed: Server did not respond that it supported either LOGIN or PLAIN as an authentication type. Use the AUTH module option to force the use of an authentication type.", MODULE_NAME);
    return FAILURE;
  }

 return SUCCESS;
}

int tryLogin(int hSocket, sLogin** psLogin, _SMTPAUTH_DATA* _psSessionData, char* szLogin, char* szPassword)
{
  int iRet;
  unsigned char bufSend[BUF_SIZE];
  unsigned char* bufReceive;
  unsigned char b64[BUF_SIZE],b642[BUF_SIZE],b64u[BUF_SIZE],b64p[BUF_SIZE];
  int m;
  /* Number of auth attempts to try per connect */
  int TRYCOUNT=2;
  int nReceiveBufferSize = 0;
  
  memset(bufSend, 0, BUF_SIZE);
  memset(b64, 0, BUF_SIZE);
  memset(b64u, 0, BUF_SIZE);
  memset(b64p, 0, BUF_SIZE);
  
  /* Build the auth plain string to send */
  memcpy(b64, szLogin, strlen(szLogin) ); 
  memcpy(b64 + strlen(szLogin) + 1 , szLogin, strlen(szLogin) );
  memcpy(b64 + strlen(szLogin) + strlen(szLogin) +2 , szPassword, strlen(szPassword) );
  m=strlen(szLogin) + strlen(szLogin) + strlen(szPassword) + 2;
  mutt_to_base64(b642,b64,m,BUF_SIZE);
  
  /* Build the auth login strings to send */
  memcpy(b64, szLogin, strlen(szLogin) ); 
  m=strlen(szLogin);
  mutt_to_base64(b64u,b64,m,BUF_SIZE);
  
  memcpy(b64, szPassword, strlen(szPassword) ); 
  m=strlen(szPassword);
  mutt_to_base64(b64p,b64,m,BUF_SIZE);
  
  if (_psSessionData->iMode == 1) {
    sprintf(bufSend, "AUTH LOGIN\r\n");
  } else {
    sprintf(bufSend, "AUTH PLAIN\r\n");
  }

  if (TRYCOUNT == 0) {
    writeError(ERR_ERROR, "[%s] trycount hit", MODULE_NAME);
    iRet = MSTATE_NEW;
    return(iRet);
  }

  /* Send the AUTH string */
  if (medusaSend(hSocket, bufSend, strlen(bufSend), 0) < 0)
  {
    writeError(ERR_ERROR, "[%s] failed: medusaSend was not successful we probably got axed", MODULE_NAME);
    (*psLogin)->iResult = LOGIN_RESULT_UNKNOWN;  
    iRet = MSTATE_NEW;
    return(iRet);

  }
 
  nReceiveBufferSize = 0;
  bufReceive = medusaReceiveRaw(hSocket, &nReceiveBufferSize);
  if (bufReceive == NULL)
  {
    writeError(ERR_ERROR, "[%s] failed: medusaReceive returned no data suspect we got axed", MODULE_NAME);
    (*psLogin)->iResult = LOGIN_RESULT_UNKNOWN;
    iRet = MSTATE_NEW;
    return(iRet);

  }
  
  /* We got something back */
  

  if (strstr(bufReceive, "504") != NULL) {
    /* We have the wrong auth type WHY ARE WE HERE */
    writeError(ERR_DEBUG_MODULE, "[%s] failed: system doesn't do auth plain", MODULE_NAME);
    _psSessionData->iMode=1;
    iRet = MSTATE_NEW;
    return(iRet);
  }

  if (strncmp(bufReceive, "334", 3) == 0)
  {
    if (_psSessionData->iMode == 1) {
      sprintf(bufSend, "%s\r\n",b64u);
    } else {
      sprintf(bufSend, "%s\r\n",b642);
    }
 
    if (medusaSend(hSocket, bufSend, strlen(bufSend), 0) < 0)
    {
      writeError(ERR_ERROR, "[%s] failed: medusaSend was not successful", MODULE_NAME);
      return FAILURE;
    }
    
    nReceiveBufferSize = 0;
    bufReceive = medusaReceiveRaw(hSocket, &nReceiveBufferSize);
    if (bufReceive == NULL)
    {
      writeError(ERR_ERROR, "[%s] failed: medusaReceive returned no data.", MODULE_NAME);
      iRet = MSTATE_EXITING;
    }
    else if (strncmp(bufReceive, "535", 3) == 0)
    {
      writeError(ERR_DEBUG_MODULE, "[%s] Login attempt failed plain.", MODULE_NAME);
      (*psLogin)->iResult = LOGIN_RESULT_FAIL;
      iRet = MSTATE_RUNNING;
    }
    else if (strncmp(bufReceive, "235", 3) == 0)
    {
      writeError(ERR_DEBUG_MODULE, "[%s] Login attempt success.", MODULE_NAME);
      (*psLogin)->iResult = LOGIN_RESULT_SUCCESS;
      iRet = MSTATE_RUNNING;
    }
    else if ((strncmp(bufReceive, "334 UGF", 7) == 0) && (_psSessionData->iMode == 1))
    {
      /* We need to go one step further here */
      sprintf(bufSend, "%s\r\n", b64p);
      if (medusaSend(hSocket, bufSend, strlen(bufSend), 0) < 0)
      {
        writeError(ERR_ERROR, "[%s] failed: medusaSend was not successful", MODULE_NAME);
        iRet = MSTATE_NEW;
      }
    
      nReceiveBufferSize = 0;
      bufReceive = medusaReceiveRaw(hSocket, &nReceiveBufferSize);
      if (bufReceive == NULL)
      {
        writeError(ERR_ERROR, "[%s] failed: medusaReceive returned no data.", MODULE_NAME);
        iRet = MSTATE_NEW;
      }
      else if (strncmp(bufReceive, "535", 3) == 0)
      {
        writeError(ERR_DEBUG_MODULE, "[%s] Login attempt failed.", MODULE_NAME);
        (*psLogin)->iResult = LOGIN_RESULT_FAIL;
        iRet = MSTATE_RUNNING;
      }
      else if (strncmp(bufReceive, "235", 3) == 0)
      {
        writeError(ERR_DEBUG_MODULE, "[%s] Login attempt success.", MODULE_NAME);
        (*psLogin)->iResult = LOGIN_RESULT_SUCCESS;
        iRet = MSTATE_RUNNING;
      }
    }
    else
    {
     writeError(ERR_ERROR, "[%s] Unknown error occurred during authentication: %s", MODULE_NAME, bufReceive);
     (*psLogin)->iResult = LOGIN_RESULT_ERROR;
     iRet = MSTATE_EXITING;
    }
  }
  
  setPassResult((*psLogin), szPassword);
  return(iRet);
}


Generated by  Doxygen 1.6.0   Back to index