Bincimap 2.0.16
Easy Imapping
Loading...
Searching...
No Matches
authenticate.cc
Go to the documentation of this file.
1
7#include <string>
8#include <vector>
9
10#include <sys/types.h>
11#include <grp.h>
12#include <pwd.h>
13#include <signal.h>
14#include <unistd.h>
15#include <errno.h>
16
17// #ifndef HAVE_SYS_WAIT_H
18// #include <wait.h>
19//#else
20#include <sys/wait.h>
21//#endif
22
23#include "authenticate.h"
24#include "iodevice.h"
25#include "iofactory.h"
26#include "session.h"
27#include "convert.h"
28#include "globals.h"
29
30using namespace ::std;
31using namespace Binc;
32
33// 0 = ok
34// 1 = internal error
35// 2 = failed
36// 3 = timeout
37// -1 = abort
38//------------------------------------------------------------------------
39int Binc::authenticate(Depot &depot, const string &username,
40 const string &password, const string &challenge)
41{
42 Session &session = Session::getInstance();
43 session.setUserID(username);
44
45 // check if checkpassword is present
46 if (::access(session.unparsedArgs[0], X_OK) != 0) { // x is enough
47 bincError << "unable to start authenticator " << session.unparsedArgs[0]
48 << ": " << strerror(errno) << endl;
49 return 1;
50 }
51
52 // The information supplied on descriptor 3 is a login name
53 // terminated by \0, a password terminated by \0, a timestamp
54 // terminated by \0, and possibly more data. There are no other
55 // restrictions on the form of the login name, password, and
56 // timestamp.
57 int authintercom[2];
58 int intercomw[2];
59 int intercomr[2];
60 bool authenticated = false;
61
62 if (pipe(authintercom) == -1) {
63 session.setLastError("An error occurred when creating pipes: "
64 + string(strerror(errno)));
65 return -1;
66 }
67
68 if (pipe(intercomw) == -1) {
69 session.setLastError("An error occurred when creating pipes: "
70 + string(strerror(errno)));
71 close(authintercom[0]);
72 close(authintercom[1]);
73 return -1;
74 }
75
76 if (pipe(intercomr) == -1) {
77 session.setLastError("An error occurred when creating pipes: "
78 + string(strerror(errno)));
79 close(intercomw[0]);
80 close(intercomr[0]);
81 close(authintercom[0]);
82 close(authintercom[1]);
83 return -1;
84 }
85
86 string timestamp;
87 time_t t = time(0);
88 char *c;
89 if ((c = ctime(&t)) != 0) {
90 timestamp = c;
91 trim(timestamp);
92 } else
93 timestamp = "unknown timestamp";
94
95 string pid = to_string(session.getPid());
96
97 // execute authentication module
98 int result;
99 int childspid = fork();
100 if (childspid == -1) {
101 bincLog << "bincimap-up: pid " << pid
102 << " failed to start main server: "
103 << strerror(errno) << endl;
104 bincLog.flush();
105 return 1;
106 }
107
108 if (childspid == 0) {
109 close(authintercom[1]);
110 close(intercomr[0]);
111 close(intercomw[1]);
112
113 if (dup2(intercomr[1], 1) == -1) {
114 bincDebug << "bincimap-up: pid " << pid
115 << " authenticate(), [auth module] dup2 failed: "
116 << strerror(errno) << endl;
117 bincDebug.flush();
118 exit(111);
119 }
120
121 if (dup2(intercomw[0], 0) == -1) {
122 bincDebug << "bincimap-up: pid " << pid
123 << " authenticate(), [auth module] dup2 failed: "
124 << strerror(errno) << endl;
125 bincDebug.flush();
126 exit(111);
127 }
128
129 if (dup2(authintercom[0], 3) == -1) {
130 bincDebug << "bincimap-up: pid " << pid
131 << " authenticate(), [auth module] dup2 failed: "
132 << strerror(errno) << endl;
133 bincDebug.flush();
134 exit(111);
135 }
136
137 if (session.unparsedArgs[0] != 0) {
138 execvp(session.unparsedArgs[0], &session.unparsedArgs[0]);
139 bincDebug << "bincimap-up: pid " << pid
140 << " authenticate(), [auth module] invocation of "
141 << session.unparsedArgs[0]
142 << " failed: " << strerror(errno) << endl;
143 bincDebug.flush();
144 exit(111);
145 }
146
147 bincLog << "bincimap-up: pid " << pid
148 << " missing mandatory -- in argument list,"
149 " after bincimap-up + arguments, before authenticator."
150 " Please check your run scripts and the man page bincimap(1) for"
151 " more on how to invoke Binc IMAP." << endl;
152 bincLog.flush();
153 bincDebug.flush();
154 exit(111);
155 }
156
157 close(authintercom[0]);
158
159 // create the string of data to be passed to the checkpassword stub
160 int dataSize = username.length() + password.length() + challenge.length() + timestamp.length();
161 dataSize += 4;
162 char *checkpasswordData = new char[dataSize];
163 char *cpTmp = checkpasswordData;
164 strcpy(cpTmp, username.c_str());
165 cpTmp += username.length();
166 *cpTmp++ = '\0';
167 strcpy(cpTmp, password.c_str());
168 cpTmp += password.length();
169 *cpTmp++ = '\0';
170 // add challenge
171 strcpy(cpTmp, challenge.c_str());
172 cpTmp += challenge.length();
173 *cpTmp++ = '\0';
174 strcpy(cpTmp, timestamp.c_str());
175 cpTmp += timestamp.length();
176 *cpTmp++ = '\0';
177
178 bincDebug << "bincimap-up: pid " << pid
179 << " authenticate(), writing username/password to "
180 << session.unparsedArgs[0] << endl;
181
182 // write the userid
183 signal(SIGPIPE, SIG_IGN);
184 int res = write(authintercom[1], checkpasswordData, dataSize);
185 delete[] checkpasswordData;
186 if (res != dataSize) {
187 bincWarning << "bincimap-up: pid " << pid
188 << " error writing to authenticator "
189 << session.unparsedArgs[0] << ": "
190 << strerror(errno) << endl;
191 return 1;
192 }
193
194 // close the write channel. this is necessary for the checkpassword
195 // module to see an EOF.
196 close(authintercom[1]);
197 close(intercomr[1]);
198 close(intercomw[0]);
199
200 fd_set rmask;
201 FD_ZERO(&rmask);
202 FD_SET(fileno(stdin), &rmask);
203 FD_SET(intercomr[0], &rmask);
204
205 int maxfd = intercomr[0];
206 bool disconnected = false;
207 bool timedout = false;
209
210 bool eof = false;
211 while (!eof) {
212 fd_set rtmp = rmask;
213 struct timeval timeout;
214
215 // time out 5 minutes after the idle timeout. we expect the main
216 // server to time out at the right time, but will shut down at
217 // T+5m in case of a server lockup.
218 timeout.tv_sec = IDLE_TIMEOUT + AUTH_PENALTY * AUTH_TIMEOUT;
219 timeout.tv_usec = 0;
220
221 // select sometimes returns when we attach to the process with
222 // tracing tools such as ktrace and strace, setting errno to
223 // EINTR.
224 int n;
225 do {
226 n = select(maxfd + 1, &rtmp, 0, 0, &timeout);
227 } while (n < 0 && errno == EINTR);
228
229 if (n < 0) {
230 bincWarning << "bincimpa-up: pid " << pid
231 << " error: invalid exit from select, "
232 << strerror(errno) << endl;
233 break;
234 }
235
236 if (n == 0) {
237 bincLog << "bincimap-up: pid " << pid
238 << " server timed out after "
239 << IDLE_TIMEOUT << " seconds" << endl;
240 bincLog.flush();
241 timedout = true;
242 break;
243 }
244
245 if (FD_ISSET(fileno(stdin), &rtmp)) {
246 authenticated = true;
247
248 do {
249 string data;
250 int ret = bincClient.readStr(&data);
251 if (ret == 0 || ret == -1) {
252 session.setLastError("client disconnected");
253 eof = true;
254 disconnected = true;
255 break;
256 }
257
258 // Fall through. Triggered when there was no data
259 // to read, even though no error has occurred
260 if (ret == -2) continue;
261
262 int w;
263 do {
264 w = write(intercomw[1], data.c_str(), data.length());
265 } while (w < 0 && errno == EINTR);
266
267 if (w > 0) Session::getInstance().addReadBytes(w);
268
269 if (w < 0) {
270 bincDebug << "bincimap-up: pid " << pid
271 << " error writing to server: "
272 << strerror(errno) << endl;
273 eof = true;
274 }
275 } while (bincClient.canRead());
276 }
277
278 if (FD_ISSET(intercomr[0], &rtmp)) {
279 char buf[8192];
280 int ret = read(intercomr[0], buf, sizeof(buf));
281 if (ret == 0) {
282 // Main server has shut down
283 eof = true;
284 break;
285 } else if (ret == -1) {
286 bincDebug << "bincimap-up: pid " << pid
287 << " error reading from server: "
288 << strerror(errno) << endl;
289 eof = true;
290 break;
291 } else {
292 // umask(0);
294
295 bincClient << string(buf, ret);
296 bincClient.flush();
297 }
298 }
299 }
300
301 close(intercomr[0]);
302 close(intercomw[1]);
303
304 // catch the dead baby
305 if (waitpid(childspid, &result, 0) != childspid) {
306 bincLog << "bincimap-up: pid " << pid
307 << " <" << username << "> authentication failed: "
308 << (authenticated ? "server " : session.unparsedArgs[0])
309 << " waitpid returned unexpected value" << endl;
310 string tmp = strerror(errno);
311
312 return -1;
313 }
314
315 // if the server died because we closed the sockets after a timeout,
316 // exit 3.
317 if (timedout) return 3;
318
319 if (disconnected) return 0;
320
321 if (WIFSIGNALED(result)) {
322 bincLog << "bincimap-up: pid " << pid
323 << " <" << username << "> authentication failed: "
324 << (authenticated ? "server" : session.unparsedArgs[0])
325 << " died by signal " << WTERMSIG(result) << endl;
326 bincLog.flush();
327 sleep(AUTH_PENALTY);
328 session.setState(Session::LOGOUT);
329 return -1;
330 }
331
332 bincDebug << "bincimap-up: pid " << pid
333 << " authenticate() ,"
334 << (authenticated ? "authenticator" : "server")
335 << " exited with code " << WEXITSTATUS(result) << endl;
336
337 switch (WEXITSTATUS(result)) {
338 case 0: break;
339 case 1:
340 // authentication failed - sleep
341 bincLog << "bincimap-up: pid " << pid
342 << " <" << username << "> failed to log in" << endl;
343 bincLog.flush();
344 sleep(AUTH_PENALTY);
345 return 2;
346 case 2: case 111: // wrong call or missing auth data
347 // abused
348 bincLog << "bincimap-up: pid " << pid
349 << " <" << username << "> authentication failed: "
350 << (authenticated ? "authenticator" : "server")
351 << " reports wrong usage" << endl;
352 bincLog.flush();
353 return 2;
354 default:
355 // internal error -- or authenticator fooled us
356 bincLog << "bincimap-up: pid " << pid
357 << " <" << username << "> authentication failed: "
358 << (authenticated ? "authenticator" : "server")
359 << " returned " << WEXITSTATUS(result) << endl;
360 bincLog.flush();
361 return -1;
362 }
363
364 return 0;
365}
Declaration of the common authentication mechanism.
void setLastError(const std::string &error) const
Definition: session.cc:185
void setUserID(const std::string &s)
Definition: session.cc:58
void setState(int n)
Definition: session.cc:46
char ** unparsedArgs
Definition: session.h:25
void addReadBytes(int)
Definition: session.cc:100
void addWriteBytes(int)
Definition: session.cc:106
pid_t getPid(void)
Definition: session.cc:209
static Session & getInstance(void)
Definition: session.cc:33
Declaration of miscellaneous convertion functions.
Global constants.
Declaration of the IODevice class.
Declaration of the IOFactory class.
#define bincDebug
Definition: iofactory.h:46
#define bincError
Definition: iofactory.h:42
#define bincLog
Definition: iofactory.h:50
#define bincWarning
Definition: iofactory.h:44
#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)
void trim(std::string &s_in, const std::string &chars=" \t\r\n")
Definition: convert.h:137