1
|
/*
|
2
|
* mod_auth_imap: authentication via IMAP server
|
3
|
* Version: 2.2 - BETA release.
|
4
|
*
|
5
|
* brillat-apachemodule@mainsheet.org (Ben Brillat),
|
6
|
* based on mod_auth_pop by Milos Prodanovic <awl@verat.net>
|
7
|
*
|
8
|
* Jian Zhen <jlz@zhen.org>
|
9
|
* Modified version 1.1 by Ben to work with Apache 2
|
10
|
*
|
11
|
* Sunding Wei <swei(at)dingding.me>
|
12
|
* Added Auth_IMAP_Domain option to login Google Apps, 2013.2.21
|
13
|
*
|
14
|
* Follows the IMAP v4rev1 RFC - RFC 2060
|
15
|
* http://www.ietf.org/rfc/rfc2060.txt
|
16
|
*/
|
17
|
|
18
|
#include "apr_lib.h"
|
19
|
|
20
|
#define APR_WANT_STRFUNC
|
21
|
#include "apr_want.h"
|
22
|
#include "apr_strings.h"
|
23
|
#include "apr_dbm.h"
|
24
|
#include "apr_md5.h"
|
25
|
|
26
|
#include "httpd.h"
|
27
|
#include "http_config.h"
|
28
|
#include "http_core.h"
|
29
|
#include "http_log.h"
|
30
|
#include "http_protocol.h"
|
31
|
#include "http_request.h" /* for ap_hook_(check_user_id | auth_checker)*/
|
32
|
|
33
|
|
34
|
#include <signal.h>
|
35
|
#include <stdio.h>
|
36
|
#include <stdlib.h>
|
37
|
#include <string.h>
|
38
|
#include <errno.h>
|
39
|
#include <sys/types.h>
|
40
|
#include <sys/socket.h>
|
41
|
#include <netinet/in.h>
|
42
|
#include <sys/types.h>
|
43
|
#include <sys/stat.h>
|
44
|
#include <fcntl.h>
|
45
|
#include <netdb.h>
|
46
|
#include <syslog.h>
|
47
|
|
48
|
#define _OK 1
|
49
|
|
50
|
int Sock;
|
51
|
|
52
|
|
53
|
/*******************************************************************************
|
54
|
* tcp_gets
|
55
|
* reads a line from the network (up to \n or EOF)
|
56
|
* Always returns 0
|
57
|
*******************************************************************************/
|
58
|
int tcp_gets(int s, char *res,int len) {
|
59
|
char c;
|
60
|
int cur=0,rc;
|
61
|
|
62
|
memset(res,0,len+1);
|
63
|
|
64
|
while( (rc = read(s, &c, 1))!=EOF) {
|
65
|
if(cur<len)
|
66
|
res[cur]=c;
|
67
|
cur++;
|
68
|
if(c=='\n') break;
|
69
|
}
|
70
|
|
71
|
return 0;
|
72
|
}
|
73
|
|
74
|
/*******************************************************************************
|
75
|
* clean_up
|
76
|
* closes the socket
|
77
|
*******************************************************************************/
|
78
|
void clean_up(int s) {
|
79
|
close(s);
|
80
|
}
|
81
|
|
82
|
/*******************************************************************************
|
83
|
* tcp_puts
|
84
|
* write a line to the network
|
85
|
* performs a bzero (via memset) on the memory to be used...
|
86
|
*******************************************************************************/
|
87
|
int tcp_puts(int s, char *sttr) {
|
88
|
int rr,len;
|
89
|
char line[512];
|
90
|
|
91
|
memset(line,0,512);
|
92
|
len=strlen(sttr);
|
93
|
if(len>510) len=510;
|
94
|
strncpy(line,sttr,len);
|
95
|
rr=write(s,line,strlen(line));
|
96
|
return rr;
|
97
|
}
|
98
|
|
99
|
|
100
|
/*******************************************************************************
|
101
|
* imap_tcp_open
|
102
|
* connects to the remote server
|
103
|
* returns a socket
|
104
|
*******************************************************************************/
|
105
|
int imap_tcp_open(request_rec *r, char *hostname, int port) {
|
106
|
struct hostent *HOST;
|
107
|
struct sockaddr_in sai;
|
108
|
int s;
|
109
|
|
110
|
if ((HOST = gethostbyname(hostname)) == (struct hostent *)NULL) {
|
111
|
ap_log_rerror(APLOG_MARK,APLOG_WARNING|APLOG_NOERRNO,0,r,"mod_auth_imap: Hostname unknown.");
|
112
|
return !_OK;
|
113
|
}
|
114
|
|
115
|
memset((char *)&sai, '\0', sizeof(sai));
|
116
|
memcpy((char *)&sai.sin_addr, (char *)HOST -> h_addr, HOST -> h_length);
|
117
|
sai.sin_family = HOST -> h_addrtype;
|
118
|
sai.sin_port = htons(port);
|
119
|
|
120
|
if ((s = socket(HOST -> h_addrtype, SOCK_STREAM, 0)) == -1) {
|
121
|
ap_log_rerror(APLOG_MARK,APLOG_WARNING|APLOG_NOERRNO,0,r,"mod_auth_imap: socket problem");
|
122
|
clean_up(s);
|
123
|
return !_OK;
|
124
|
}
|
125
|
|
126
|
if(connect(s, (struct sockaddr *)&sai, sizeof(sai)) == -1) {
|
127
|
ap_log_rerror(APLOG_MARK,APLOG_WARNING|APLOG_NOERRNO,0,r,"mod_auth_imap: connect() problem");
|
128
|
clean_up(s);
|
129
|
return !_OK;
|
130
|
}
|
131
|
|
132
|
return s;
|
133
|
}
|
134
|
|
135
|
|
136
|
/******************************************************************
|
137
|
*imap_do_rfc2060
|
138
|
* Check the U/P against the selected IMAP server
|
139
|
* according to IMAP v4rev1 RFC
|
140
|
* Return: 1 or 0
|
141
|
******************************************************************/
|
142
|
int imap_do_rfc2060(request_rec *r, char *host, char *username, char *pass,
|
143
|
char *cport, int logflag) {
|
144
|
char result[512],buf[512];
|
145
|
int ret=0;
|
146
|
int port;
|
147
|
|
148
|
port=atoi(cport);
|
149
|
|
150
|
// Verify that the username and password are of a reasonable length (<100)
|
151
|
if( strlen(username)>100 || strlen(pass)>100 ) {
|
152
|
ap_log_rerror(APLOG_MARK,APLOG_WARNING|APLOG_NOERRNO,0,r,"username/password too long for mod_auth_imap");
|
153
|
printf("Ouch - u/p too long!\n");
|
154
|
return !_OK;
|
155
|
}
|
156
|
|
157
|
Sock = imap_tcp_open(r, host, port);
|
158
|
|
159
|
if (!Sock) return !_OK;
|
160
|
|
161
|
//Just eat the initial response from the server, up to 500 chars.
|
162
|
tcp_gets(Sock,result,500);
|
163
|
|
164
|
//Send a request for CAPABILITY
|
165
|
memset(buf,0,500);
|
166
|
sprintf(buf,"A001 CAPABILITY\r\n");
|
167
|
tcp_puts(Sock,buf);
|
168
|
|
169
|
//get the capability line...
|
170
|
tcp_gets(Sock,result,500);
|
171
|
|
172
|
//get the "A001 OK CAPABILITY completed" line..
|
173
|
tcp_gets(Sock,result,500);
|
174
|
|
175
|
//skip lines that start with "*"
|
176
|
if (strncmp(result,"* ",2 == 0)) {
|
177
|
tcp_gets(Sock,result,500);
|
178
|
}
|
179
|
|
180
|
//Verify that it supports the CAPABILITY command
|
181
|
if (strncmp(result,"A001 OK", 7) != 0) {
|
182
|
ap_log_rerror(APLOG_MARK,APLOG_WARNING|APLOG_NOERRNO,0,r,"mod_auth_imap: Server does not support imap CAPABILITY.");
|
183
|
ret=!_OK;
|
184
|
clean_up(Sock);
|
185
|
return ret; //BAIL!
|
186
|
}
|
187
|
|
188
|
//Log In w/given Username & Password
|
189
|
memset(buf,0,500);
|
190
|
sprintf(buf,"A002 LOGIN %s \"%s\"\r\n", username, pass);
|
191
|
tcp_puts(Sock,buf);
|
192
|
tcp_gets(Sock,result,500);
|
193
|
|
194
|
if (strncmp(result,"A002 OK",7) == 0) {
|
195
|
if (logflag) {
|
196
|
ap_log_rerror(APLOG_MARK,APLOG_WARNING|APLOG_NOERRNO,0,r,"mod_auth_imap: Verified login for user %s.", username);
|
197
|
}
|
198
|
|
199
|
ret=_OK;
|
200
|
} else if (strncmp(result,"A002 NO",7) == 0) {
|
201
|
if (logflag) {
|
202
|
ap_log_rerror(APLOG_MARK,APLOG_WARNING|APLOG_NOERRNO,0,r,"mod_auth_imap: Login failed for user %s.", username);
|
203
|
ap_log_rerror(APLOG_MARK,APLOG_WARNING|APLOG_NOERRNO,0,r,"mod_auth_imap: Server said: %s", result);
|
204
|
}
|
205
|
|
206
|
ret=!_OK;
|
207
|
} else {
|
208
|
//it must have told us BYE and disconnected
|
209
|
if (logflag) {
|
210
|
ap_log_rerror(APLOG_MARK,APLOG_WARNING|APLOG_NOERRNO,0,r,"mod_auth_imap: Premature server disconnect for user %s.", username);
|
211
|
ap_log_rerror(APLOG_MARK,APLOG_WARNING|APLOG_NOERRNO,0,r,"mod_auth_imap: Server said: %s", result);
|
212
|
}
|
213
|
|
214
|
ret=!_OK;
|
215
|
clean_up(Sock);
|
216
|
return ret; //BAIL!
|
217
|
}
|
218
|
|
219
|
//Log out...
|
220
|
memset(buf,0,500);
|
221
|
sprintf(buf,"A003 LOGOUT\r\n");
|
222
|
tcp_puts(Sock,buf);
|
223
|
|
224
|
//read the BYE line
|
225
|
tcp_gets(Sock,result,500);
|
226
|
|
227
|
//read the OK LOGOUT
|
228
|
tcp_gets(Sock,result,500);
|
229
|
|
230
|
if (strncmp(result,"A003 OK",7) == 0) {
|
231
|
if (logflag) {
|
232
|
ap_log_rerror(APLOG_MARK,APLOG_WARNING|APLOG_NOERRNO,0,r,"mod_auth_imap: OK logout for %s.", username);
|
233
|
}
|
234
|
//don't change the return here - we still need to know if
|
235
|
//the user/pass was good from the LOGIN command!
|
236
|
} else {
|
237
|
if (logflag) {
|
238
|
ap_log_rerror(APLOG_MARK,APLOG_WARNING|APLOG_NOERRNO,0,r,"mod_auth_imap: Error in logout for %s.", username);
|
239
|
ap_log_rerror(APLOG_MARK,APLOG_WARNING|APLOG_NOERRNO,0,r,"mod_auth_imap: Server said: %s", result);
|
240
|
}
|
241
|
|
242
|
ret=!_OK;
|
243
|
}
|
244
|
|
245
|
//close the connection
|
246
|
clean_up(Sock);
|
247
|
|
248
|
return ret;
|
249
|
}
|
250
|
|
251
|
|
252
|
/******************************************************************
|
253
|
* auth_config_struct
|
254
|
* The struct with all data passed from .htaccess or httpd.conf
|
255
|
******************************************************************/
|
256
|
typedef struct auth_config_struct {
|
257
|
char *imap_server;
|
258
|
char *imap_port;
|
259
|
char *imap_domain; /* Using email as username if domain provided */
|
260
|
int imap_log;
|
261
|
int imap_authoritative;
|
262
|
int imap_enabled;
|
263
|
} imap_config_rec;
|
264
|
|
265
|
|
266
|
/******************************************************************
|
267
|
* Defaults for the .htaccess options
|
268
|
******************************************************************/
|
269
|
static void *create_imap_dir_config (apr_pool_t *p, char *d)
|
270
|
{
|
271
|
imap_config_rec *sec =
|
272
|
(imap_config_rec *) apr_palloc (p, sizeof(imap_config_rec));
|
273
|
sec->imap_server = "127.0.0.1"; /* localhost is server by default */
|
274
|
sec->imap_port = "143"; /* port is 143 by default */
|
275
|
sec->imap_domain = "";
|
276
|
sec->imap_log = 0; /* Logging is off by default */
|
277
|
sec->imap_authoritative = 1; /* keep the fortress secure by default */
|
278
|
sec->imap_enabled = 0; /* disable this authentication by defualt */
|
279
|
return sec;
|
280
|
}
|
281
|
|
282
|
|
283
|
/*******************************************************************************
|
284
|
* .htaccess and httpd.conf configuration options
|
285
|
*******************************************************************************/
|
286
|
static command_rec imap_cmds[] = {
|
287
|
AP_INIT_TAKE1("Auth_IMAP_Server", ap_set_string_slot,
|
288
|
(void *)APR_OFFSETOF(imap_config_rec, imap_server), OR_AUTHCFG,
|
289
|
"IMAP server . " ),
|
290
|
|
291
|
AP_INIT_TAKE1("Auth_IMAP_Port", ap_set_string_slot,
|
292
|
(void *)APR_OFFSETOF(imap_config_rec, imap_port), OR_AUTHCFG,
|
293
|
"IMAP port . " ),
|
294
|
|
295
|
AP_INIT_TAKE1("Auth_IMAP_Domain", ap_set_string_slot,
|
296
|
(void *)APR_OFFSETOF(imap_config_rec, imap_domain), OR_AUTHCFG,
|
297
|
"IMAP domain . " ),
|
298
|
|
299
|
AP_INIT_FLAG("Auth_IMAP_Log", ap_set_flag_slot,
|
300
|
(void *)APR_OFFSETOF(imap_config_rec, imap_log), OR_AUTHCFG,
|
301
|
"Set to 'yes' to enable some logging in the Apache ErrorLog" ),
|
302
|
|
303
|
AP_INIT_FLAG("Auth_IMAP_Authoritative", ap_set_flag_slot,
|
304
|
(void *)APR_OFFSETOF(imap_config_rec, imap_authoritative), OR_AUTHCFG,
|
305
|
"Set to 'no' to allow access control to be passed along to lower modules if the UserID is not known to this module" ),
|
306
|
|
307
|
AP_INIT_FLAG("Auth_IMAP_Enabled", ap_set_flag_slot,
|
308
|
(void *)APR_OFFSETOF(imap_config_rec, imap_enabled), OR_AUTHCFG,
|
309
|
"on|off - determines if IMAP authentication is enabled; default is off" ),
|
310
|
|
311
|
{ NULL }
|
312
|
};
|
313
|
|
314
|
|
315
|
|
316
|
|
317
|
|
318
|
|
319
|
module AP_MODULE_DECLARE_DATA auth_imap_module;
|
320
|
|
321
|
|
322
|
/*******************************************************************************
|
323
|
* imap_authenticate_basic_user - the main function
|
324
|
* connects remotely, verifies if the user/pass combo is correct
|
325
|
* returns "OK", "DECLINED", or "HTTP_UNAUTHORIZED" (see defines)
|
326
|
*******************************************************************************/
|
327
|
static int imap_authenticate_basic_user (request_rec *r)
|
328
|
{
|
329
|
imap_config_rec *sec =
|
330
|
(imap_config_rec *)ap_get_module_config (r->per_dir_config, &auth_imap_module);
|
331
|
int res,i,u;
|
332
|
char *server = sec->imap_server;
|
333
|
char *port = sec->imap_port;
|
334
|
char *domain = sec->imap_domain;
|
335
|
char *email = NULL;
|
336
|
char *user = r->user;
|
337
|
const char *sent_pw;
|
338
|
|
339
|
/* is this module disabled? */
|
340
|
if (!sec->imap_enabled) return DECLINED;
|
341
|
|
342
|
if ((res = ap_get_basic_auth_pw (r, &sent_pw))) return res;
|
343
|
|
344
|
if(!sec->imap_server) return DECLINED;
|
345
|
|
346
|
/* Email as username */
|
347
|
if (domain && *domain) {
|
348
|
u = strlen(domain) + strlen(r->user) + 2;
|
349
|
email = malloc(u);
|
350
|
snprintf(email, u, "%s@%s", r->user, domain);
|
351
|
user = email;
|
352
|
}
|
353
|
|
354
|
/* send IMAP authen, server port, username, sent_pw */
|
355
|
i=imap_do_rfc2060(r,server,user,sent_pw,port,sec->imap_log);
|
356
|
if (email != NULL)
|
357
|
free(email);
|
358
|
|
359
|
if (i==1) return (OK);
|
360
|
|
361
|
ap_note_basic_auth_failure(r);
|
362
|
return HTTP_UNAUTHORIZED;
|
363
|
}
|
364
|
|
365
|
|
366
|
/*******************************************************************************
|
367
|
* imap_check_user_access
|
368
|
* checks the username against the allowed users in the .htaccess or httpd.conf
|
369
|
* returns "OK", "DECLINED", or "HTTP_UNAUTHORIZED" (see defines)
|
370
|
*******************************************************************************/
|
371
|
static int imap_check_user_access (request_rec *r) {
|
372
|
imap_config_rec *sec =
|
373
|
(imap_config_rec *)ap_get_module_config (r->per_dir_config, &auth_imap_module);
|
374
|
char *user = r->user;
|
375
|
int m = r->method_number;
|
376
|
int method_restricted = 0;
|
377
|
register int x;
|
378
|
const char *t, *w;
|
379
|
const apr_array_header_t *reqs_arr = ap_requires (r);
|
380
|
require_line *reqs;
|
381
|
|
382
|
if (!reqs_arr)
|
383
|
return (OK);
|
384
|
reqs = (require_line *)reqs_arr->elts;
|
385
|
|
386
|
for(x=0; x < reqs_arr->nelts; x++) {
|
387
|
|
388
|
if (! (reqs[x].method_mask & (1 << m))) continue;
|
389
|
|
390
|
method_restricted = 1;
|
391
|
|
392
|
t = reqs[x].requirement;
|
393
|
w = ap_getword(r->pool, &t, ' ');
|
394
|
if(!strcmp(w,"valid-user"))
|
395
|
return OK;
|
396
|
if(!strcmp(w,"user")) {
|
397
|
while(t[0]) {
|
398
|
w = ap_getword_conf (r->pool, &t);
|
399
|
if(!strcmp(user,w))
|
400
|
return OK;
|
401
|
}
|
402
|
}
|
403
|
else if(!strcmp(w,"group"))
|
404
|
return DECLINED;
|
405
|
}
|
406
|
|
407
|
if (!method_restricted)
|
408
|
return OK;
|
409
|
|
410
|
if (!(sec->imap_authoritative))
|
411
|
return DECLINED;
|
412
|
|
413
|
ap_note_basic_auth_failure (r);
|
414
|
return HTTP_UNAUTHORIZED;
|
415
|
}
|
416
|
|
417
|
static void imap_register_hooks(apr_pool_t *p) {
|
418
|
ap_hook_check_user_id(imap_authenticate_basic_user, NULL, NULL,
|
419
|
APR_HOOK_MIDDLE);
|
420
|
ap_hook_auth_checker(imap_check_user_access, NULL, NULL, APR_HOOK_MIDDLE);
|
421
|
}
|
422
|
|
423
|
/*******************************************************************************
|
424
|
* module function definitions
|
425
|
* defines the functions called by apache for this module...
|
426
|
*******************************************************************************/
|
427
|
module AP_MODULE_DECLARE_DATA auth_imap_module = {
|
428
|
STANDARD20_MODULE_STUFF,
|
429
|
create_imap_dir_config, /* dir config creater */
|
430
|
NULL, /* dir merger --- default is to override */
|
431
|
NULL, /* server config */
|
432
|
NULL, /* merge server config */
|
433
|
imap_cmds, /* command table */
|
434
|
imap_register_hooks /* register hooks */
|
435
|
};
|
436
|
|