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.smtpExtension;
022:
023: import java.io.UnsupportedEncodingException;
024: import java.security.MessageDigest;
025: import java.security.NoSuchAlgorithmException;
026: import java.util.Hashtable;
027:
028: import javax.naming.Context;
029: import javax.naming.InitialContext;
030: import javax.naming.NamingEnumeration;
031: import javax.naming.NamingException;
032: import javax.naming.directory.Attribute;
033: import javax.naming.directory.Attributes;
034: import javax.naming.directory.DirContext;
035: import javax.naming.directory.SearchControls;
036: import javax.naming.directory.SearchResult;
037:
038: import org.apache.commons.logging.Log;
039: import org.apache.commons.logging.LogFactory;
040: import org.jsmtpd.core.common.PluginInitException;
041: import org.vps.crypt.Crypt;
042:
043: import sun.misc.BASE64Encoder;
044:
045: /**
046: * 1/04/06 : Change to query primary mail by fixed attribute, not by uid (for multiple domains and outgoing)
047: * @author jf poux
048: *
049: */
050: public class LdapAuthenticator extends SmtpAuthenticator {
051: private static Log log = LogFactory.getLog(LdapAuthenticator.class);
052:
053: private String adminBindDn; // ex cn=administrator,dc=jsmtpd,dc=org
054: private String adminBindPassword;
055: private String ldapUrl;
056:
057: // How to query the user db
058: private String ldapUserProvider;
059: private String ldapUserPassword;
060: /**
061: * By default, will use uid attribute (posix/shadow account schema), in this case login = uid
062: * If you want vdom, set to primary mail attribute (should be unique in the directory).
063: * Users will then provide their primary mail as login.
064: */
065: private String ldapUserLogin = "uid";
066: private MessageDigest md;
067:
068: protected boolean performAuth(String login, byte[] password) {
069: Hashtable<String, String> environnement;
070: environnement = new Hashtable<String, String>();
071: environnement.put(Context.SECURITY_PRINCIPAL, adminBindDn);
072: environnement.put(Context.SECURITY_CREDENTIALS,
073: adminBindPassword);
074:
075: InitialContext initialContext;
076: try {
077: initialContext = new InitialContext(environnement);
078: DirContext ctx = (DirContext) initialContext
079: .lookup(ldapUrl);
080: SearchControls searchControl = new SearchControls();
081: NamingEnumeration<SearchResult> namingEnumeration = ctx
082: .search(ldapUserProvider, "(" + ldapUserLogin + "="
083: + login + ")", searchControl);
084: while (namingEnumeration.hasMore()) {
085: SearchResult result = namingEnumeration.next();
086: Attributes attributes = result.getAttributes();
087: Attribute ldapAttrPass = attributes
088: .get(ldapUserPassword);
089: String ldapPass = new String((byte[]) ldapAttrPass
090: .get(), "UTF8");
091: if (ldapPass == null)
092: return false;
093:
094: if (ldapPass.startsWith("{md5}")) {
095: String pwd = ldapPass.substring(5);
096: byte[] bytePass = md.digest(password);
097: BASE64Encoder b = new BASE64Encoder();
098: String inputPass = b.encode(bytePass);
099: if (pwd.equals(inputPass)) {
100: log.debug("user " + login
101: + " authenticated (md5 password)");
102: return true;
103: } else {
104: log
105: .debug("user "
106: + login
107: + " authentication failed (md5 password)");
108: return false;
109: }
110: }
111:
112: if (ldapPass.startsWith("{crypt}")) {
113: String full = ldapPass.substring(7);
114: String salt = full.substring(0, 2);
115: String rv = Crypt.crypt(salt.getBytes(), password);
116:
117: if (rv.equals(full)) {
118: log.debug("user " + login
119: + " authenticated (crypt password)");
120: return true;
121: } else {
122: log
123: .debug("user "
124: + login
125: + " authentication failed (crypt password)");
126: return false;
127: }
128: }
129:
130: log
131: .error("I don't know how to handle encryption for user "
132: + login + " in ldap entry");
133: }
134: } catch (NamingException e) {
135: log.error("Can't query server for aliases", e);
136: } catch (UnsupportedEncodingException e) {
137: log.error("Can't convert enc password", e);
138: }
139:
140: return false;
141: }
142:
143: public String getPluginName() {
144: return "Ldap Authenticator for Jsmtpd";
145: }
146:
147: public void initPlugin() throws PluginInitException {
148: try {
149: md = MessageDigest.getInstance("md5");
150: } catch (NoSuchAlgorithmException e) {
151: throw new PluginInitException("No md5 available");
152: }
153: }
154:
155: public void shutdownPlugin() {
156: }
157:
158: public void setLdapUrl(String ldapUrl) {
159: this .ldapUrl = ldapUrl;
160: }
161:
162: public void setLdapUserLogin(String ldapUserLogin) {
163: this .ldapUserLogin = ldapUserLogin;
164: }
165:
166: public void setLdapUserPassword(String ldapUserPassword) {
167: this .ldapUserPassword = ldapUserPassword;
168: }
169:
170: public void setLdapUserProvider(String ldapUserProvider) {
171: this .ldapUserProvider = ldapUserProvider;
172: }
173:
174: public void setAdminBindDn(String adminBindDn) {
175: this .adminBindDn = adminBindDn;
176: }
177:
178: public void setAdminBindPassword(String adminBindPassword) {
179: this.adminBindPassword = adminBindPassword;
180: }
181:
182: }
|