001: /*
002: *
003: * Jsmtpd, Java SMTP daemon
004: * Copyright (C) 2005 Jean-Francois POUX, jf.poux@laposte.net
005: *
006: * This program is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU General Public License
008: * as published by the Free Software Foundation; either version 2
009: * of the License, or (at your option) any later version.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: * GNU General Public License for more details.
015: *
016: * You should have received a copy of the GNU General Public License
017: * along with this program; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
019: *
020: */
021: package org.jsmtpd.plugins.acls;
022:
023: import java.net.Inet4Address;
024: import java.net.InetAddress;
025: import java.net.UnknownHostException;
026: import java.util.HashSet;
027: import java.util.Hashtable;
028: import java.util.Set;
029:
030: import javax.naming.Context;
031: import javax.naming.InitialContext;
032: import javax.naming.NamingEnumeration;
033: import javax.naming.NamingException;
034: import javax.naming.directory.Attribute;
035: import javax.naming.directory.Attributes;
036: import javax.naming.directory.DirContext;
037: import javax.naming.directory.SearchControls;
038: import javax.naming.directory.SearchResult;
039:
040: import org.apache.commons.logging.Log;
041: import org.apache.commons.logging.LogFactory;
042: import org.jsmtpd.core.common.PluginInitException;
043: import org.jsmtpd.core.common.acl.ExtendedInet4Address;
044: import org.jsmtpd.core.common.acl.IACL;
045: import org.jsmtpd.core.mail.EmailAddress;
046:
047: /**
048: * 1/04/06 : Change to query primary mail by fixed attribute, not by uid (for multiple domains and outgoing)
049: * TODO: Cache results with ex date
050: * @author jf poux
051: *
052: */
053: public class LdapACL implements IACL {
054:
055: private String adminBindDn; // ex cn=administrator,dc=jsmtpd,dc=org
056: private String adminBindPassword;
057: private String ldapUrl; // ex ldap://172.16.0.200/
058:
059: // How to query the user db
060: private String ldapUserProvider; // Branch containing user objects, like ou=people,dc=jsmtpd,dc=org
061: private String ldapUserAliasAttribute = "rfc822MailAlias"; // Attribute of mail aliases
062: private String ldapUserPrimaryMail = "mail"; // Attribute of primary mail address.
063:
064: private String ldapNetworkProvider; // A branch containing network objects (class ipNetwork by example) : ou=networks,dc=jsmtpd,dc=org
065: private String ldapNetworkClass = "ipNetwork";
066: private String ldapNetworkAddressAttribute = "ipNetworkNumber"; // Atribute of network ip
067: private String ldapNetworkMaskAttribute = "ipNetmaskNumber"; // attribut of network mask
068:
069: private Hashtable<String, String> environnement;
070: private static Log log = LogFactory.getLog(LdapACL.class);
071:
072: public void initPlugin() throws PluginInitException {
073: environnement = new Hashtable<String, String>();
074: environnement.put(Context.SECURITY_PRINCIPAL, adminBindDn);
075: environnement.put(Context.SECURITY_CREDENTIALS,
076: adminBindPassword);
077: }
078:
079: public boolean isValidAddress(EmailAddress e) {
080: if (isValidAddressWildCard(e))
081: return true;
082: else
083: return isValidAddressStandardUser(e);
084: }
085:
086: public boolean isValidAddressStandardUser(EmailAddress e) {
087: InitialContext initialContext = null;
088: NamingEnumeration<SearchResult> namingEnumeration = null;
089: DirContext ctx = null;
090: try {
091: initialContext = new InitialContext(environnement);
092: ctx = (DirContext) initialContext.lookup(ldapUrl);
093: SearchControls searchControl = new SearchControls();
094:
095: // Will look for primary mail field.
096: String ldapQuery = "(" + ldapUserPrimaryMail + "="
097: + e.toString() + ")";
098: namingEnumeration = ctx.search(ldapUserProvider, ldapQuery,
099: searchControl);
100: boolean found = namingEnumeration.hasMore();
101: namingEnumeration.close();
102: if (found) {
103: log.debug("User " + e.toString()
104: + " found in directory");
105: return true;
106: }
107:
108: // Lookup alias
109: ldapQuery = "(" + ldapUserAliasAttribute + "="
110: + e.toString() + ")";
111: namingEnumeration = ctx.search(ldapUserProvider, ldapQuery,
112: searchControl);
113: found = namingEnumeration.hasMore();
114: namingEnumeration.close();
115: if (found) {
116: log.debug("Alias " + e.toString()
117: + " found in directory");
118: return true;
119: }
120:
121: } catch (NamingException e1) {
122: log.error("Failed to query server", e1);
123: } finally {
124: try {
125: if (namingEnumeration != null)
126: namingEnumeration.close();
127: if (ctx != null)
128: ctx.close();
129: if (initialContext != null)
130: initialContext.close();
131: } catch (NamingException e1) {
132: log.error(e1);
133: }
134: }
135: return false;
136: }
137:
138: public boolean isValidAddressWildCard(EmailAddress e) {
139: InitialContext initialContext = null;
140: DirContext ctx = null;
141: NamingEnumeration<SearchResult> namingEnumeration = null;
142: try {
143: initialContext = new InitialContext(environnement);
144: ctx = (DirContext) initialContext.lookup(ldapUrl);
145: SearchControls searchControl = new SearchControls();
146:
147: // see if there are users with *@domain.com wildcard aliases
148: String query = "(" + ldapUserAliasAttribute + "=@"
149: + e.getHost() + ")";
150: namingEnumeration = ctx.search(ldapUserProvider, query,
151: searchControl);
152: boolean found = namingEnumeration.hasMore();
153: namingEnumeration.close();
154: if (found) {
155: log.debug("Wildcard alias " + e.toString()
156: + " found in directory");
157: return true;
158: }
159:
160: } catch (NamingException e1) {
161: log.error("Failed to query server", e1);
162: } finally {
163: try {
164: if (namingEnumeration != null)
165: namingEnumeration.close();
166: if (ctx != null)
167: ctx.close();
168: if (initialContext != null)
169: ctx.close();
170: } catch (NamingException e1) {
171: log.error(e1);
172: }
173: }
174: return false;
175: }
176:
177: public void shutdownPlugin() {
178:
179: }
180:
181: public boolean isValidRelay(String hostIP) {
182:
183: InitialContext initialContext = null;
184: DirContext ctx = null;
185: NamingEnumeration<SearchResult> namingEnumeration = null;
186: // This set is rebuilt for each query. Can be cached with expiraton time
187: Set<ExtendedInet4Address> ldapNetworks = new HashSet<ExtendedInet4Address>();
188: Inet4Address ag;
189: try {
190: initialContext = new InitialContext(environnement);
191: ctx = (DirContext) initialContext.lookup(ldapUrl);
192: SearchControls searchControl = new SearchControls();
193: namingEnumeration = ctx.search(ldapNetworkProvider,
194: "(objectclass=" + ldapNetworkClass + ")",
195: searchControl);
196: while (namingEnumeration.hasMore()) {
197: SearchResult result = namingEnumeration.next();
198: Attributes attributes = result.getAttributes();
199: Attribute ip = attributes
200: .get(ldapNetworkAddressAttribute);
201: Attribute mask = attributes
202: .get(ldapNetworkMaskAttribute);
203:
204: if ((ip == null) || (mask == null))
205: throw new NamingException(
206: "Can't fing ip or netmask");
207:
208: Inet4Address ipInet = (Inet4Address) InetAddress
209: .getByName((String) ip.get());
210: Inet4Address maskInet = (Inet4Address) InetAddress
211: .getByName((String) mask.get());
212: ExtendedInet4Address ad = new ExtendedInet4Address(
213: ipInet, maskInet);
214: ldapNetworks.add(ad);
215: }
216: ag = (Inet4Address) InetAddress.getByName(hostIP);
217: for (ExtendedInet4Address address : ldapNetworks) {
218: if (address.isEqualorInMask(ag))
219: return true;
220: }
221: } catch (NamingException e) {
222: log.error("Can't query server for relays", e);
223: } catch (UnknownHostException e) {
224: log.error("Conversion of ip failed ", e);
225: } finally {
226: try {
227: if (namingEnumeration != null)
228: namingEnumeration.close();
229: if (ctx != null)
230: ctx.close();
231: if (initialContext != null)
232: ctx.close();
233: } catch (NamingException e1) {
234: log.error(e1);
235: }
236: }
237: return false;
238: }
239:
240: public String getPluginName() {
241: return "Simple LDAP ACL provider for jsmtpd";
242: }
243:
244: public void setAdminBindDn(String adminBindDn) {
245: this .adminBindDn = adminBindDn;
246: }
247:
248: public void setAdminBindPassword(String adminBindPassword) {
249: this .adminBindPassword = adminBindPassword;
250: }
251:
252: public void setLdapNetworkAddressAttribute(
253: String ldapNetworkAddressAttribute) {
254: this .ldapNetworkAddressAttribute = ldapNetworkAddressAttribute;
255: }
256:
257: public void setLdapNetworkClass(String ldapNetworkClass) {
258: this .ldapNetworkClass = ldapNetworkClass;
259: }
260:
261: public void setLdapNetworkMaskAttribute(
262: String ldapNetworkMaskAttribute) {
263: this .ldapNetworkMaskAttribute = ldapNetworkMaskAttribute;
264: }
265:
266: public void setLdapNetworkProvider(String ldapNetworkProvider) {
267: this .ldapNetworkProvider = ldapNetworkProvider;
268: }
269:
270: public void setLdapUrl(String ldapUrl) {
271: this .ldapUrl = ldapUrl;
272: }
273:
274: public void setLdapUserAliasAttribute(String ldapUserAliasAttribute) {
275: this .ldapUserAliasAttribute = ldapUserAliasAttribute;
276: }
277:
278: public void setLdapUserPrimaryMail(String ldapUserPrimaryMail) {
279: this .ldapUserPrimaryMail = ldapUserPrimaryMail;
280: }
281:
282: public void setLdapUserProvider(String ldapUserProvider) {
283: this.ldapUserProvider = ldapUserProvider;
284: }
285:
286: }
|