/* * mod_auth_imap: authentication via IMAP server * Version: 1.1 - BETA release. * * brillat-apachemodule@mainsheet.org (Ben Brillat), * based on mod_auth_pop by Milos Prodanovic * * Follows the IMAP v4rev1 RFC - RFC 2060 * http://www.ietf.org/rfc/rfc2060.txt */ #include "httpd.h" #include "http_config.h" #include "http_core.h" #include "http_log.h" #include "http_protocol.h" #include "http_conf_globals.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #define _OK 1 int Sock; /******************************************************************************* * tcp_gets * reads a line from the network (up to \n or EOF) * Always returns 0 *******************************************************************************/ int tcp_gets(int s, char *res,int len) { char c; int cur=0,rc; memset(res,0,len+1); while( (rc = read(s, &c, 1))!=EOF) { if(cur510) len=510; strncpy(line,sttr,len); rr=write(s,line,strlen(line)); return rr; } /******************************************************************************* * imap_tcp_open * connects to the remote server * returns a socket *******************************************************************************/ int imap_tcp_open(char *hostname, int port) { struct hostent *HOST; struct sockaddr_in sai; int s; if((HOST = gethostbyname(hostname)) == (struct hostent *)NULL) { ap_log_error(APLOG_MARK,APLOG_WARNING|APLOG_NOERRNO,NULL,"mod_auth_imap: Hostname unknown."); return !_OK; } memset((char *)&sai, '\0', sizeof(sai)); memcpy((char *)&sai.sin_addr, (char *)HOST -> h_addr, HOST -> h_length); sai.sin_family = HOST -> h_addrtype; sai.sin_port = htons(port); if((s = socket(HOST -> h_addrtype, SOCK_STREAM, 0)) == -1) { ap_log_error(APLOG_MARK,APLOG_WARNING|APLOG_NOERRNO,NULL,"mod_auth_imap: socket problem"); clean_up(s); return !_OK; } if(connect(s, (struct sockaddr *)&sai, sizeof(sai)) == -1) { ap_log_error(APLOG_MARK,APLOG_WARNING|APLOG_NOERRNO,NULL,"mod_auth_imap: connect() problem"); clean_up(s); return !_OK; } return s; } /****************************************************************** *imap_do_rfc2060 * Check the U/P against the selected IMAP server * according to IMAP v4rev1 RFC * Return: 1 or 0 ******************************************************************/ int imap_do_rfc2060(char *host, char *username, char *pass, char *cport, int logflag) { char result[512],buf[512]; int ret=0; int port; port=atoi(cport); // Verify that the username and password are of a reasonable length (<100) if( strlen(username)>100 || strlen(pass)>100 ) { ap_log_error(APLOG_MARK,APLOG_WARNING|APLOG_NOERRNO,NULL,"username/password too long for mod_auth_imap"); printf("Ouch - u/p too long!\n"); return !_OK; } Sock=imap_tcp_open(host,port); if(!Sock) { return !_OK; } //Just eat the initial response from the server, up to 500 chars. tcp_gets(Sock,result,500); //Send a request for CAPABILITY memset(buf,0,500); sprintf(buf,"A001 CAPABILITY\r\n"); tcp_puts(Sock,buf); //get the capability line... tcp_gets(Sock,result,500); //get the "A001 OK CAPABILITY completed" line.. tcp_gets(Sock,result,500); if(strncmp(result,"A001 OK CAPABILITY",18)) { ap_log_error(APLOG_MARK,APLOG_WARNING|APLOG_NOERRNO,NULL,"mod_auth_imap: Server does not support imap CAPABILITY."); ret=!_OK; clean_up(Sock); return ret; //BAIL! } //Log In w/given Username & Password memset(buf,0,500); sprintf(buf,"A002 LOGIN %s %s\r\n", username, pass); tcp_puts(Sock,buf); tcp_gets(Sock,result,500); if(!strncmp(result,"A002 OK",7)) { if(logflag){ ap_log_error(APLOG_MARK,APLOG_WARNING|APLOG_NOERRNO,NULL,"mod_auth_imap: Verified login for user %s.", username); } ret=_OK; } else if(!strncmp(result,"A002 NO",7)) { if(logflag){ ap_log_error(APLOG_MARK,APLOG_WARNING|APLOG_NOERRNO,NULL,"mod_auth_imap: Login failed for user %s.", username); ap_log_error(APLOG_MARK,APLOG_WARNING|APLOG_NOERRNO,NULL,"mod_auth_imap: Server said: %s", result); } ret=!_OK; } else //it must have told us BYE and disconnected { if(logflag){ ap_log_error(APLOG_MARK,APLOG_WARNING|APLOG_NOERRNO,NULL,"mod_auth_imap: Premature server disconnect for user %s.", username); ap_log_error(APLOG_MARK,APLOG_WARNING|APLOG_NOERRNO,NULL,"mod_auth_imap: Server said: %s", result); } ret=!_OK; clean_up(Sock); return ret; //BAIL! } //Log out... memset(buf,0,500); sprintf(buf,"A003 LOGOUT\r\n"); tcp_puts(Sock,buf); //read the BYE line tcp_gets(Sock,result,500); //read the OK LOGOUT tcp_gets(Sock,result,500); if(!strncmp(result,"A003 OK",7)) { /*if(logflag){ ap_log_error(APLOG_MARK,APLOG_WARNING|APLOG_NOERRNO,NULL,"mod_auth_imap: OK logout for %s.", username); }*/ //don't change the return here - we still need to know if //the user/pass was good from the LOGIN command! } else { if(logflag){ ap_log_error(APLOG_MARK,APLOG_WARNING|APLOG_NOERRNO,NULL,"mod_auth_imap: Error in logout for %s.", username); ap_log_error(APLOG_MARK,APLOG_WARNING|APLOG_NOERRNO,NULL,"mod_auth_imap: Server said: %s", result); } ret=!_OK; } //close the connection clean_up(Sock); return ret; } /****************************************************************** * auth_config_struct * The struct with all data passed from .htaccess or httpd.conf ******************************************************************/ typedef struct auth_config_struct { char *imap_server; char *imap_port; int imap_log; int imap_authoritative; int enabled; } imap_config_rec; typedef struct { char *name, *pw; } auth_imap_userinfo; /****************************************************************** * Defaults for the .htaccess options ******************************************************************/ static void *create_imap_dir_config (pool *p, char *d) { imap_config_rec *sec = (imap_config_rec *) ap_palloc (p, sizeof(imap_config_rec)); sec->imap_server = "127.0.0.1"; /* localhost is server by default */ sec->imap_port = "143"; /* port is 143 by default */ sec->imap_log = 0; /* Logging is off by default */ sec->imap_authoritative = 1; /* keep the fortress secure by default */ sec->enabled = 0; /* disable this authentication by defualt */ return sec; } /******************************************************************************* * set_imap_slot *******************************************************************************/ static const char *set_imap_slot (cmd_parms *cmd, char *struct_ptr, char *arg) { int offset = (int)cmd->info; *(char **)(struct_ptr + offset) = ap_pstrdup (cmd->pool, arg); return NULL; } /******************************************************************************* * set_flag_slot - transfers the Auth_IMAP_Authoritative directive to the structure *******************************************************************************/ static const char *set_flag_slot (cmd_parms *cmd, imap_config_rec *sec, char *arg) { /*warning - the next line generates warnings. it's ok. */ sec->imap_authoritative = (unsigned char) arg; return NULL; } /******************************************************************************* * set_log_slot - transfers the Auth_IMAP_Log directive to the structure *******************************************************************************/ static const char *set_log_slot (cmd_parms *cmd, imap_config_rec *sec, char *arg) { /*warning - the next line generates warnings. it's ok. */ sec->imap_log = (unsigned char) arg; return NULL; } /******************************************************************************* * auth_enable *******************************************************************************/ static char* auth_enable(cmd_parms *cmd, imap_config_rec *config, int arg) { config->enabled = arg; return NULL; } /******************************************************************************* * .htaccess and httpd.conf configuration options *******************************************************************************/ static command_rec imap_cmds[] = { { "Auth_IMAP_Server", set_imap_slot, (void*)XtOffsetOf(imap_config_rec,imap_server), OR_AUTHCFG, TAKE1, "IMAP server . " }, { "Auth_IMAP_Port", set_imap_slot, (void*)XtOffsetOf(imap_config_rec,imap_port), OR_AUTHCFG, TAKE1, "IMAP port . " }, { "Auth_IMAP_Log", set_log_slot, (void*)XtOffsetOf(imap_config_rec,imap_log), OR_AUTHCFG, FLAG, "Set to 'yes' to enable some logging in the Apache ErrorLog" }, { "Auth_IMAP_Authoritative", set_flag_slot, (void*)XtOffsetOf(imap_config_rec,imap_authoritative), OR_AUTHCFG, FLAG, "Set to 'no' to allow access control to be passed along to lower modules if the UserID is not known to this module" }, { "Auth_IMAP_Enabled", (const char*(*)())auth_enable, NULL, OR_AUTHCFG, FLAG, "on|off - determines if IMAP authentication is enabled; default is off" }, { NULL } }; module auth_imap_module; /******************************************************************************* * imap_authenticate_basic_user - the main function * connects remotely, verifies if the user/pass combo is correct * returns "OK", "DECLINED", or "AUTH_REQUIRED" (see defines) *******************************************************************************/ static int imap_authenticate_basic_user (request_rec *r) { imap_config_rec *sec = (imap_config_rec *)ap_get_module_config (r->per_dir_config, &auth_imap_module); conn_rec *c = r->connection; int res,i; char *server = sec->imap_server; char *port = sec->imap_port; auth_imap_userinfo userinfo = { NULL, NULL }; /* is this module disabled? */ if (!sec->enabled) return DECLINED; if ((res = ap_get_basic_auth_pw (r, (const char**)&(userinfo.pw)))) return res; /* this is only set after get_basic_auth_pw was called */ userinfo.name = r->connection->user; if(!sec->imap_server) return DECLINED; /* send IMAP authen, server port, username, sent_pw */ i=imap_do_rfc2060(server,userinfo.name,userinfo.pw,port,sec->imap_log); if (i==1) return (OK); else return AUTH_REQUIRED; } /******************************************************************************* * imap_check_user_access * checks the username against the allowed users in the .htaccess or httpd.conf * returns "OK", "DECLINED", or "AUTH_REQUIRED" (see defines) *******************************************************************************/ static int imap_check_user_access (request_rec *r) { imap_config_rec *sec = (imap_config_rec *)ap_get_module_config (r->per_dir_config, &auth_imap_module); char *user = r->connection->user; int m = r->method_number; int method_restricted = 0; register int x; const char *t, *w; array_header *reqs_arr = ap_requires (r); require_line *reqs; if (!reqs_arr) return (OK); reqs = (require_line *)reqs_arr->elts; for(x=0; x < reqs_arr->nelts; x++) { if (! (reqs[x].method_mask & (1 << m))) continue; method_restricted = 1; t = reqs[x].requirement; w = ap_getword(r->pool, &t, ' '); if(!strcmp(w,"valid-user")) return OK; if(!strcmp(w,"user")) { while(t[0]) { w = ap_getword_conf (r->pool, &t); if(!strcmp(user,w)) return OK; } } else if(!strcmp(w,"group")) return DECLINED; } if (!method_restricted) return OK; if (!(sec->imap_authoritative)) return DECLINED; ap_note_basic_auth_failure (r); return AUTH_REQUIRED; } /******************************************************************************* * module function definitions * defines the functions called by apache for this module... *******************************************************************************/ module auth_imap_module = { STANDARD_MODULE_STUFF, NULL, /* initializer */ create_imap_dir_config, /* dir config creater */ NULL, /* dir merger --- default is to override */ NULL, /* server config */ NULL, /* merge server config */ imap_cmds, /* command table */ NULL, /* handlers */ NULL, /* filename translation */ imap_authenticate_basic_user, /* check_user_id */ imap_check_user_access, /* check auth */ NULL, /* check access */ NULL, /* type_checker */ NULL, /* fixups */ NULL, /* logger */ NULL /* header parser */ };