/* -*- Mode: c++; -*- */
/* --------------------------------------------------------------------
* Filename:
* operator-authenticate.cc
*
* Description:
* Implementation of the AUTHENTICATE command.
*
* Authors:
* Andreas Aardal Hanssen <andreas-binc curly bincimap spot org>
*
* Bugs:
*
* ChangeLog:
*
* --------------------------------------------------------------------
* Copyright 2002-2005 Andreas Aardal Hanssen
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
* --------------------------------------------------------------------
*/
#include <string>
#include "authenticate.h"
#include "depot.h"
#include "io.h"
#include "session.h"
#include "operators.h"
#include "recursivedescent.h"
#include "base64.h"
#include "convert.h"
using namespace ::std;
using namespace Binc;
//----------------------------------------------------------------------
AuthenticateOperator::AuthenticateOperator(void)
{
}
//----------------------------------------------------------------------
AuthenticateOperator::~AuthenticateOperator(void)
{
}
//----------------------------------------------------------------------
const string AuthenticateOperator::getName(void) const
{
return "AUTHENTICATE";
}
//----------------------------------------------------------------------
int AuthenticateOperator::getState(void) const
{
return Session::NONAUTHENTICATED;
}
//------------------------------------------------------------------------
Operator::ProcessResult AuthenticateOperator::process(Depot &depot,
Request &command)
{
Session &session = Session::getInstance();
IO &com = IOFactory::getInstance().get(1);
string authtype = command.getAuthType();
uppercase(authtype);
string username;
string password;
bool allowplain = (session.globalconfig["Authentication"]["allow plain auth in non ssl"] == "yes");
// for now, we only support LOGIN.
if (authtype == "LOGIN") {
// we only allow this type of authentication over a plain
// connection if it is passed as argument or given in the conf
// file.
if (!session.command.ssl && session["sslmode"] != "yes" && !allowplain && !getenv("ALLOWPLAIN")) {
session.setLastError("Plain text password authentication"
" is disallowed. Please try enabling SSL"
" or TLS in your mail client.");
return NO;
}
com << "+ " << base64encode("User Name") << endl;
com.flushContent();
// Read user name
for (;;) {
int c = com.readChar();
if (c == -1)
return BAD;
if ((char) c == '\n')
break;
username += c;
}
if (username != "" && username[0] == '*') {
session.setLastError("Authentication cancelled by user");
return NO;
}
com << "+ " << base64encode("Password") << endl;
com.flushContent();
// Read password
for (;;) {
int c = com.readChar();
if (c == -1)
return BAD;
if ((char)c == '\n')
break;
password += c;
}
if (password != "" && password[0] == '*') {
session.setLastError("Authentication cancelled by user");
return NO;
}
username = base64decode(username);
password = base64decode(password);
} else if (authtype == "PLAIN") {
// we only allow this type of authentication over an SSL encrypted
// connection.
if (!session.command.ssl && session["sslmode"] != "yes" && !allowplain && !getenv("ALLOWPLAIN")) {
session.setLastError("Plain text password authentication"
" is disallowed. Please try enabling SSL"
" or TLS in your mail client.");
return NO;
}
com << "+ " << endl;
com.flushContent();
string b64;
for (;;) {
int c = com.readChar();
if (c == -1) {
session.setLastError("unexpected EOF");
return BAD;
}
if ((char) c == '\r') {
com.readChar();
break;
}
b64 += (char) c;
}
if (b64.size() >= 1 && b64[0] == '*') {
session.setLastError("Authentication cancelled by user");
return NO;
}
string plain = base64decode(b64);
string::size_type pos;
if ((pos = plain.find('\0')) == string::npos) {
session.setLastError("Authentication failed. In PLAIN mode,"
" there must be at least two null characters"
" in the input string, but none were found");
return NO;
}
plain = plain.substr(pos + 1);
if ((pos = plain.find('\0')) == string::npos) {
session.setLastError("Authentication failed. In PLAIN mode,"
" there must be at least two null characters"
" in the input string, but only one was found");
return NO;
}
username = plain.substr(0, pos);
password = plain.substr(pos + 1);
} else {
session.setLastError("The authentication method "
+ toImapString(authtype) + " is not supported."
" Please try again with a different method."
" There is built in support for \"PLAIN\""
" and \"LOGIN\".");
return NO;
}
putenv(strdup(("BINCIMAP_LOGIN=AUTHENTICATE+" + command.getTag()).c_str()));
// the authenticate function calls a stub which does the actual
// authentication. the function returns 0 (success), 1 (internal
// error) or 2 (failed)
switch (authenticate(depot,
(const string &)username,
(const string &)password)) {
case 1:
session.setLastError("An internal error occurred when you attempted"
" to log in to the IMAP server. Please contact"
" your system administrator.");
return NO;
case 2:
session.setLastError("Login failed. Either your user name"
" or your password was wrong. Please try again,"
" and if the problem persists, please contact"
" your system administrator.");
return NO;
case 3:
com << "* BYE Timeout after " << session.idletimeout
<< " seconds of inactivity." << endl;
break;
case -1:
com << "* BYE The server died unexpectedly. Please contact "
"your system administrator for more information." << endl;
break;
}
// auth was ok. go to logout state
session.setState(Session::LOGOUT);
return NOTHING;
}
//----------------------------------------------------------------------
Operator::ParseResult AuthenticateOperator::parse(Request &c_in) const
{
Session &session = Session::getInstance();
if (c_in.getUidMode())
return REJECT;
Operator::ParseResult res;
if ((res = expectSPACE()) != ACCEPT) {
session.setLastError("Expected single SPACE after AUTHENTICATE");
return res;
}
string authtype;
if ((res = expectAtom(authtype)) != ACCEPT) {
session.setLastError("Expected auth_type after AUTHENTICATE SPACE");
return ERROR;
}
if ((res = expectCRLF()) != ACCEPT) {
session.setLastError("Expected CRLF after AUTHENTICATE SPACE auth_type");
return res;
}
c_in.setAuthType(authtype);
c_in.setName("AUTHENTICATE");
return ACCEPT;
}
syntax highlighted by Code2HTML, v. 0.9.1