bincimap

Log | Files | Refs | LICENSE

operator-authenticate.cc (7577B)


      1 /* -*- Mode: c++; -*- */
      2 /*  --------------------------------------------------------------------
      3  *  Filename:
      4  *    operator-authenticate.cc
      5  *  
      6  *  Description:
      7  *    Implementation of the AUTHENTICATE command.
      8  *
      9  *  Authors:
     10  *    Andreas Aardal Hanssen <andreas-binc curly bincimap spot org>
     11  *
     12  *  Bugs:
     13  *
     14  *  ChangeLog:
     15  *
     16  *  --------------------------------------------------------------------
     17  *  Copyright 2002-2005 Andreas Aardal Hanssen
     18  *
     19  *  This program is free software; you can redistribute it and/or modify
     20  *  it under the terms of the GNU General Public License as published by
     21  *  the Free Software Foundation; either version 2 of the License, or
     22  *  (at your option) any later version.
     23  *
     24  *  This program is distributed in the hope that it will be useful,
     25  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     26  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     27  *  GNU General Public License for more details.
     28  *
     29  *  You should have received a copy of the GNU General Public License
     30  *  along with this program; if not, write to the Free Software
     31  *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
     32  *  --------------------------------------------------------------------
     33  */
     34 #include <string>
     35 
     36 #include "authenticate.h"
     37 #include "depot.h"
     38 #include "io.h"
     39 #include "session.h"
     40 #include "operators.h"
     41 #include "recursivedescent.h"
     42 #include "base64.h"
     43 #include "convert.h"
     44 
     45 using namespace ::std;
     46 using namespace Binc;
     47 
     48 //----------------------------------------------------------------------
     49 AuthenticateOperator::AuthenticateOperator(void)
     50 {
     51 }
     52 
     53 //----------------------------------------------------------------------
     54 AuthenticateOperator::~AuthenticateOperator(void)
     55 {
     56 }
     57 
     58 //----------------------------------------------------------------------
     59 const string AuthenticateOperator::getName(void) const
     60 {
     61   return "AUTHENTICATE";
     62 }
     63 
     64 //----------------------------------------------------------------------
     65 int AuthenticateOperator::getState(void) const
     66 {
     67   return Session::NONAUTHENTICATED;
     68 }
     69 
     70 //------------------------------------------------------------------------
     71 Operator::ProcessResult AuthenticateOperator::process(Depot &depot, 
     72 						      Request &command)
     73 {
     74   Session &session = Session::getInstance();
     75   IO &com = IOFactory::getInstance().get(1);
     76 
     77   string authtype = command.getAuthType();
     78   uppercase(authtype);
     79 
     80   string username;
     81   string password;
     82 
     83   bool allowplain = (session.globalconfig["Authentication"]["allow plain auth in non ssl"] == "yes");
     84     
     85 
     86   // for now, we only support LOGIN.
     87   if (authtype == "LOGIN") {
     88     // we only allow this type of authentication over a plain
     89     // connection if it is passed as argument or given in the conf
     90     // file.
     91     if (!session.command.ssl && session["sslmode"] != "yes" && !allowplain && !getenv("ALLOWPLAIN")) {
     92       session.setLastError("Plain text password authentication"
     93 			   " is disallowed. Please try enabling SSL"
     94 			   " or TLS in your mail client.");
     95       return NO;
     96     }
     97     
     98     com << "+ " << base64encode("User Name") << endl;
     99     com.flushContent();
    100 
    101     // Read user name
    102     for (;;) {
    103       int c = com.readChar();
    104       if (c == -1)
    105 	return BAD;
    106 
    107       if ((char) c == '\n')
    108 	break;
    109 
    110       username += c;
    111     }
    112     
    113     if (username != "" && username[0] == '*') {
    114       session.setLastError("Authentication cancelled by user");
    115       return NO;
    116     }
    117     
    118     com << "+ " << base64encode("Password") << endl;
    119     com.flushContent();
    120 
    121     // Read password    
    122     for (;;) {
    123       int c = com.readChar();
    124       if (c == -1)
    125 	return BAD;
    126 
    127       if ((char)c == '\n')
    128 	break;
    129        
    130       password += c;
    131     }
    132     
    133     if (password != "" && password[0] == '*') {
    134       session.setLastError("Authentication cancelled by user");
    135       return NO;
    136     }
    137 
    138     username = base64decode(username);
    139     password = base64decode(password);
    140 
    141   } else if (authtype == "PLAIN") {
    142     // we only allow this type of authentication over an SSL encrypted
    143     // connection.
    144     if (!session.command.ssl && session["sslmode"] != "yes" && !allowplain && !getenv("ALLOWPLAIN")) {
    145       session.setLastError("Plain text password authentication"
    146 			   " is disallowed. Please try enabling SSL"
    147 			   " or TLS in your mail client.");
    148       return NO;
    149     }
    150 
    151     com << "+ " << endl;
    152     com.flushContent();
    153     
    154     string b64;
    155     for (;;) {
    156       int c = com.readChar();
    157       if (c == -1) {
    158 	session.setLastError("unexpected EOF");
    159 	return BAD;
    160       }
    161 
    162       if ((char) c == '\r') {
    163 	com.readChar();
    164 	break;
    165       }
    166       
    167       b64 += (char) c;
    168     }
    169 
    170     if (b64.size() >= 1 && b64[0] == '*') {
    171       session.setLastError("Authentication cancelled by user");
    172       return NO;
    173     }
    174 
    175     string plain = base64decode(b64);
    176     string::size_type pos;
    177     if ((pos = plain.find('\0')) == string::npos) {
    178       session.setLastError("Authentication failed. In PLAIN mode,"
    179 			   " there must be at least two null characters"
    180 			   " in the input string, but none were found");
    181       return NO;
    182     }
    183 
    184     plain = plain.substr(pos + 1);
    185     if ((pos = plain.find('\0')) == string::npos) {
    186       session.setLastError("Authentication failed. In PLAIN mode,"
    187 			   " there must be at least two null characters"
    188 			   " in the input string, but only one was found");
    189       return NO;
    190     }
    191 
    192     username = plain.substr(0, pos);
    193     password = plain.substr(pos + 1);
    194   } else {
    195     session.setLastError("The authentication method " 
    196 			 + toImapString(authtype) + " is not supported."
    197 			 " Please try again with a different method."
    198 			 " There is built in support for \"PLAIN\""
    199 			 " and \"LOGIN\".");
    200     return NO;
    201   }
    202 
    203   putenv(strdup(("BINCIMAP_LOGIN=AUTHENTICATE+" + command.getTag()).c_str()));
    204 
    205   // the authenticate function calls a stub which does the actual
    206   // authentication. the function returns 0 (success), 1 (internal
    207   // error) or 2 (failed)
    208   switch (authenticate(depot,
    209 		       (const string &)username,
    210 		       (const string &)password)) {
    211   case 1:
    212     session.setLastError("An internal error occurred when you attempted"
    213 			 " to log in to the IMAP server. Please contact"
    214 			 " your system administrator.");
    215     return NO;
    216   case 2:
    217     session.setLastError("Login failed. Either your user name"
    218 			 " or your password was wrong. Please try again,"
    219 			 " and if the problem persists, please contact"
    220 			 " your system administrator.");
    221     return NO;
    222   case 3:    
    223     com << "* BYE Timeout after " << session.idletimeout
    224 	<< " seconds of inactivity." << endl;
    225     break;
    226   case -1:
    227     com << "* BYE The server died unexpectedly. Please contact "
    228       "your system administrator for more information." << endl;
    229     break;
    230 
    231   }
    232 
    233   // auth was ok. go to logout state
    234   session.setState(Session::LOGOUT);
    235   return NOTHING;
    236 }
    237 
    238 
    239 //----------------------------------------------------------------------
    240 Operator::ParseResult AuthenticateOperator::parse(Request &c_in) const
    241 {
    242   Session &session = Session::getInstance();
    243 
    244   if (c_in.getUidMode())
    245     return REJECT;
    246 
    247   Operator::ParseResult res;
    248 
    249   if ((res = expectSPACE()) != ACCEPT) {
    250     session.setLastError("Expected single SPACE after AUTHENTICATE");
    251     return res;
    252   }
    253 
    254   string authtype;
    255   if ((res = expectAtom(authtype)) != ACCEPT) {
    256     session.setLastError("Expected auth_type after AUTHENTICATE SPACE");
    257     return ERROR;
    258   }
    259 
    260   if ((res = expectCRLF()) != ACCEPT) {
    261     session.setLastError("Expected CRLF after AUTHENTICATE SPACE auth_type");
    262     return res;
    263   }
    264 
    265   c_in.setAuthType(authtype);
    266 
    267   c_in.setName("AUTHENTICATE");
    268   return ACCEPT;
    269 }