Bincimap 2.0.16
Easy Imapping
Loading...
Searching...
No Matches
operator-authenticate.cc
Go to the documentation of this file.
1
7#include <string>
8
9#include "authenticate.h"
10#include "base64.h"
11#include "convert.h"
12#include "depot.h"
13#include "iodevice.h"
14#include "iofactory.h"
15#include "globals.h"
16#include "operators.h"
17#include "recursivedescent.h"
18#include "session.h"
19#include <cstring>
20
21using namespace ::std;
22using namespace Binc;
23
24//----------------------------------------------------------------------
26{
27}
28
29//----------------------------------------------------------------------
31{
32}
33
34//----------------------------------------------------------------------
35const string AuthenticateOperator::getName(void) const
36{
37 return "AUTHENTICATE";
38}
39
40//----------------------------------------------------------------------
42{
44}
45
46//------------------------------------------------------------------------
47Operator::ProcessResult AuthenticateOperator::Login(string& username, string& password)
48{
49 Session &session = Session::getInstance();
50
51 bincClient << "+ " << base64encode("User Name") << endl;
52 bincClient.flush();
53
54 // Read user name
55 string b64usr;
56 for (;;) {
57 char c;
58 if (!bincClient.readChar(&c)) {
59 session.setLastError("unexpected EOF");
60 return BAD;
61 }
62 if (c == '\n') break;
63 b64usr += c;
64 }
65
66 if (b64usr != "" && b64usr[0] == '*') {
67 session.setLastError("Authentication cancelled by user");
68 return NO;
69 }
70
71 bincClient << "+ " << base64encode("Password") << endl;
72 bincClient.flush();
73
74 // Read password
75 string b64pwd;
76 for (;;) {
77 char c;
78 if (!bincClient.readChar(&c)) {
79 session.setLastError("unexpected EOF");
80 return BAD;
81 }
82 if (c == '\n') break;
83 b64pwd += c;
84 }
85
86 if (b64pwd != "" && b64pwd[0] == '*') {
87 session.setLastError("Authentication cancelled by user");
88 return NO;
89 }
90
91 username = base64decode(b64usr);
92 password = base64decode(b64pwd);
93 session.setEnv("AUTH", "AUTH::Login");
94
95 return OK;
96}
97//------------------------------------------------------------------------
98Operator::ProcessResult AuthenticateOperator::Plain(string& username, string& password)
99{
100 Session &session = Session::getInstance();
101
102 bincClient << "+ " << endl;
103 bincClient.flush();
104
105 string b64;
106 for (;;) {
107 char c;
108 if (!bincClient.readChar(&c)) {
109 session.setLastError("unexpected EOF");
110 return BAD;
111 }
112 if (c == '\n') break;
113
114 b64 += c;
115 }
116
117 if (b64.size() >= 1 && b64[0] == '*') {
118 session.setLastError("Authentication cancelled by user");
119 return NO;
120 }
121
122 string plain = base64decode(b64);
123 string::size_type pos = 0;
124
125 if ((pos = plain.find('\0')) == string::npos) {
126 session.setLastError("Authentication failed. In PLAIN mode, "
127 "there must be at least two null characters "
128 "in the input string, but none were found");
129 return NO;
130 }
131
132 plain = plain.substr(pos + 1);
133 if ((pos = plain.find('\0')) == string::npos) {
134 session.setLastError("Authentication failed. In PLAIN mode, "
135 "there must be at least two null characters "
136 "in the input string, but only one was found");
137 return NO;
138 }
139
140 username = plain.substr(0, pos);
141 password = plain.substr(pos + 1);
142 session.setEnv("AUTH", "AUTH::Plain");
143
144 return OK;
145}
146//------------------------------------------------------------------------
147Operator::ProcessResult AuthenticateOperator::Cram(string& username, string& password,
148 string& challenge)
149{
150 Session &session = Session::getInstance();
151
152 // generate challenge first: <pid.time@fqdn> and deploy it to authenticator
153 time_t timer;
154 struct tm y2k = {0};
155 int timestamp;
156 y2k.tm_hour = 0; y2k.tm_min = 0; y2k.tm_sec = 0;
157 y2k.tm_year = 100; y2k.tm_mon = 0; y2k.tm_mday = 1;
158
159 time(&timer); /* get current time; same as: timer = time(NULL) */
160 timestamp = difftime(timer,mktime(&y2k));
161
162 challenge += "<";
163 challenge += to_string(session.getPid());
164 challenge += ".";
165 challenge += to_string(timestamp);
166 challenge += "@";
167 challenge += session.getEnv("TCPLOCALHOST");
168 challenge += ">";
169
170 bincClient << "+ " << base64encode(challenge) << endl;
171 bincClient.flush();
172
173 // Read response
174 string b64;
175 for (;;) {
176 char c;
177 if (!bincClient.readChar(&c)) return BAD;
178 if (c == '\n') break;
179 b64 += c;
180 }
181
182 // Disentangle response
183 string response = base64decode(b64);
184 string::size_type pos = 0;
185
186 if ((pos = response.find(' ')) == string::npos) {
187 session.setLastError("Authentication failed. In CRAM-MD5 mode, "
188 "there must be a white space in the "
189 "input string between username and digest");
190 return NO;
191 }
192
193 username = response.substr(0, pos);
194 password = response.substr(pos + 1);
195 session.setEnv("AUTH", "AUTH::CramMD5");
196
197 return OK;
198}
199//------------------------------------------------------------------------
201 Request &command)
202{
203 Session &session = Session::getInstance();
204
205 string authtype = command.getAuthType();
206 uppercase(authtype);
207
208 string username;
209 string password;
210 string challenge;
212
213 if (authtype == "LOGIN") {
214 // we only allow this type of authentication over an unencryted connection
215 // if it is explicitely commanded
216 if (!session.command.ssl
217 && !session.hasEnv("ALLOW_NONSSL_PLAINTEXT_LOGINS")) {
218 session.setLastError("Plain text password authentication is disallowd. "
219 "Please enable StartTLS or TLS in your mail client.");
220 return NO;
221 }
222 if ((r = Login(username, password)) != OK) return r;
223
224 } else if (authtype == "PLAIN") {
225 // we only allow this type of authentication over an TLS encrypted connection.
226 if (!session.command.ssl
227 && !session.hasEnv("ALLOW_NONSSL_PLAINTEXT_LOGINS")) {
228 session.setLastError("Plain text password authentication is disallowd. "
229 "Please enable StartTLS or TLS in your mail client.");
230 return NO;
231 }
232 if ((r = Plain(username, password)) != OK) return r;
233
234 } else if (authtype == "CRAM-MD5" ) {
235 // this type can be used even over unencrypted connections
236 if ((r = Cram(username, password, challenge)) != OK) return r;
237
238
239 } else { // Any other disallowed
240 session.setLastError("The authentication method "
241 + toImapString(authtype) + " is not supported. "
242 "Please try again with a different method. "
243 "There is built in support for \"PLAIN\" "
244 "and \"LOGIN\".");
245 return NO;
246 }
247
248 putenv(strdup(("BINCIMAP_LOGIN=AUTHENTICATE+" + command.getTag()).c_str()));
249
250 // put the username in the environment for logging purpose
251 session.setEnv("USER", username);
252 session.setEnv("AUTH_USER", username);
253
254 // the authenticate function calls a stub which does the actual
255 // authentication. the function returns 0 (success), 1 (internal
256 // error) or 2 (failed)
257
258 switch (authenticate(depot, username, password, challenge)) {
259 case 1:
260 session.setLastError("An internal error occurred when you attempted "
261 "to log in to the IMAP server. Please contact "
262 "your system administrator.");
263 return NO;
264 case 2:
265 session.setLastError("Login failed. Either your user name "
266 "or your password was wrong. Please try again, "
267 "and if the problem persists, please contact "
268 "your system administrator.");
269 return NO;
270 case 3:
271 bincClient << "* BYE Timeout after " << IDLE_TIMEOUT
272 << " seconds of inactivity." << endl;
273 break;
274 case -1:
275 bincClient << "* BYE The server died unexpectedly. Please contact "
276 "your system administrator for more information." << endl;
277 break;
278 default:
279// bincLog << "<" << username.c_str() << "> authenticated" << endl;
280 break;
281 }
282
283 // auth was ok. go to logout state
284 session.setState(Session::LOGOUT);
285 return NOTHING;
286}
287
288
289//----------------------------------------------------------------------
291{
292 Session &session = Session::getInstance();
293
294 if (c_in.getUidMode()) return REJECT;
295
297
298 if ((res = expectSPACE()) != ACCEPT) {
299 session.setLastError("Expected single SPACE after AUTHENTICATE");
300 return res;
301 }
302
303 string authtype;
304 if ((res = expectAtom(authtype)) != ACCEPT) {
305 session.setLastError("Expected auth_type after AUTHENTICATE SPACE");
306 return ERROR;
307 }
308
309 if ((res = expectCRLF()) != ACCEPT) {
310 session.setLastError("Expected CRLF after AUTHENTICATE SPACE auth_type");
311 return res;
312 }
313
314 c_in.setAuthType(authtype);
315
316 c_in.setName("AUTHENTICATE");
317 return ACCEPT;
318}
Declaration of the common authentication mechanism.
Declaration of base64 Utilities.
ProcessResult Plain(std::string &username, std::string &password)
virtual ParseResult parse(Request &) const
ProcessResult Cram(std::string &username, std::string &password, std::string &challenge)
ProcessResult process(Depot &, Request &)
ProcessResult Login(std::string &username, std::string &password)
const std::string getName(void) const
const std::string & getTag(void) const
Definition: imapparser.cc:52
void setName(const std::string &s_in)
Definition: imapparser.cc:70
bool getUidMode(void) const
Definition: imapparser.cc:40
void setAuthType(const std::string &s_in)
Definition: imapparser.cc:82
const std::string & getAuthType(void) const
Definition: imapparser.cc:88
void setEnv(const std::string &key, const std::string &value)
Definition: session.cc:237
void setLastError(const std::string &error) const
Definition: session.cc:185
void setState(int n)
Definition: session.cc:46
@ NONAUTHENTICATED
Definition: session.h:36
bool ssl
Definition: session.h:30
struct Binc::Session::@3 command
pid_t getPid(void)
Definition: session.cc:209
static Session & getInstance(void)
Definition: session.cc:33
std::string getEnv(const std::string &key)
Definition: session.cc:230
bool hasEnv(const std::string &key) const
Definition: session.cc:224
Declaration of miscellaneous convertion functions.
Global constants.
Declaration of the IODevice class.
Declaration of the IOFactory class.
#define bincClient
Definition: iofactory.h:31
Definition: bincimapd.cc:9
int authenticate(Depot &, const std::string &username, const std::string &password, const std::string &challenge)
std::string toImapString(const std::string &s_in)
Definition: convert.h:103
Operator::ParseResult expectSPACE(void)
std::string base64decode(const std::string &s_in)
std::string base64encode(const std::string &s_in)
Operator::ParseResult expectAtom(std::string &s_in)
void uppercase(std::string &input)
Definition: convert.h:115
Operator::ParseResult expectCRLF(void)
Declaration of all operators.
Declaration of a recursive descent IMAP command parser.