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 }