diff --git a/CHANGES b/CHANGES index 77ba80f8e..1ce9b8f97 100644 --- a/CHANGES +++ b/CHANGES @@ -2727,4 +2727,4 @@ [7.6.4] * mail: update limit plugin - +* ldap: fix error messages to show proper error messages in the external LDAP connector diff --git a/src/directoryserver.js b/src/directoryserver.js index 46d0ddd98..7e15a8ae6 100644 --- a/src/directoryserver.js +++ b/src/directoryserver.js @@ -298,7 +298,7 @@ async function userAuth(req, res, next) { // extract the common name which might have different attribute names const cnAttributeName = Object.keys(req.dn.rdns[0].attrs)[0]; const commonName = req.dn.rdns[0].attrs[cnAttributeName].value; - if (!commonName) return next(new ldap.NoSuchObjectError(req.dn.toString())); + if (!commonName) return next(new ldap.NoSuchObjectError('Missing CN')); const TOTPTOKEN_ATTRIBUTE_NAME = 'totptoken'; // This has to be in-sync with externalldap.js const totpToken = req.dn.rdns[0].attrs[TOTPTOKEN_ATTRIBUTE_NAME] ? req.dn.rdns[0].attrs[TOTPTOKEN_ATTRIBUTE_NAME].value : null; @@ -353,8 +353,8 @@ async function start() { const config = await getConfig(); - if (!req.dn.equals(constants.USER_DIRECTORY_LDAP_DN)) return next(new ldap.InvalidCredentialsError(req.dn.toString())); - if (req.credentials !== config.secret) return next(new ldap.InvalidCredentialsError(req.dn.toString())); + if (!req.dn.equals(constants.USER_DIRECTORY_LDAP_DN)) return next(new ldap.InvalidCredentialsError('Invalid DN')); + if (req.credentials !== config.secret) return next(new ldap.InvalidCredentialsError('Invalid Secret')); req.user = { user: 'directoryServerAdmin' }; diff --git a/src/externalldap.js b/src/externalldap.js index c67ac3923..bd9414040 100644 --- a/src/externalldap.js +++ b/src/externalldap.js @@ -303,7 +303,7 @@ async function verifyPassword(user, password, totpToken) { const [error] = await safe(util.promisify(client.bind.bind(client))(userAuthDn, password)); client.unbind(); - if (error instanceof ldap.InvalidCredentialsError) throw new BoxError(BoxError.INVALID_CREDENTIALS); + if (error instanceof ldap.InvalidCredentialsError) throw new BoxError(BoxError.INVALID_CREDENTIALS, error.lde_message); if (error) throw new BoxError(BoxError.EXTERNAL_ERROR, error); return translateUser(config, ldapUsers[0]); diff --git a/src/ldap.js b/src/ldap.js index e6a761e65..8f733ecdb 100644 --- a/src/ldap.js +++ b/src/ldap.js @@ -51,7 +51,7 @@ async function userAuthInternal(appId, req, res, next) { // extract the common name which might have different attribute names const attributeName = Object.keys(req.dn.rdns[0].attrs)[0]; const commonName = req.dn.rdns[0].attrs[attributeName].value; - if (!commonName) return next(new ldap.NoSuchObjectError(req.dn.toString())); + if (!commonName) return next(new ldap.NoSuchObjectError('Missing CN')); const TOTPTOKEN_ATTRIBUTE_NAME = 'totptoken'; const totpToken = req.dn.rdns[0].attrs[TOTPTOKEN_ATTRIBUTE_NAME] ? req.dn.rdns[0].attrs[TOTPTOKEN_ATTRIBUTE_NAME].value : null; @@ -363,15 +363,15 @@ async function mailboxSearch(req, res, next) { async function mailAliasSearch(req, res, next) { debug('mail alias get: dn %s, scope %s, filter %s (from %s)', req.dn.toString(), req.scope, req.filter.toString(), req.connection.ldap.id); - if (!req.dn.rdns[0].attrs.cn) return next(new ldap.NoSuchObjectError(req.dn.toString())); + if (!req.dn.rdns[0].attrs.cn) return next(new ldap.NoSuchObjectError('Missing CN')); const email = req.dn.rdns[0].attrs.cn.value.toLowerCase(); const parts = email.split('@'); - if (parts.length !== 2) return next(new ldap.NoSuchObjectError(req.dn.toString())); + if (parts.length !== 2) return next(new ldap.NoSuchObjectError('Invalid CN')); const [error, alias] = await safe(mail.searchAlias(parts[0], parts[1])); if (error) return next(new ldap.OperationsError(error.message)); - if (!alias) return next(new ldap.NoSuchObjectError(req.dn.toString())); + if (!alias) return next(new ldap.NoSuchObjectError('No such alias')); if (!alias.active) return next(new ldap.NoSuchObjectError('Mailbox is not active')); // there is no way to disable an alias. this is just here for completeness @@ -402,15 +402,15 @@ async function mailAliasSearch(req, res, next) { async function mailingListSearch(req, res, next) { debug('mailing list get: dn %s, scope %s, filter %s (from %s)', req.dn.toString(), req.scope, req.filter.toString(), req.connection.ldap.id); - if (!req.dn.rdns[0].attrs.cn) return next(new ldap.NoSuchObjectError(req.dn.toString())); + if (!req.dn.rdns[0].attrs.cn) return next(new ldap.NoSuchObjectError('Missing CN')); let email = req.dn.rdns[0].attrs.cn.value.toLowerCase(); let parts = email.split('@'); - if (parts.length !== 2) return next(new ldap.NoSuchObjectError(req.dn.toString())); + if (parts.length !== 2) return next(new ldap.NoSuchObjectError('Invalid CN')); const name = parts[0], domain = parts[1]; const [error, result] = await safe(mail.resolveList(parts[0], parts[1])); - if (error && error.reason === BoxError.NOT_FOUND) return next(new ldap.NoSuchObjectError(req.dn.toString())); + if (error && error.reason === BoxError.NOT_FOUND) return next(new ldap.NoSuchObjectError('No such list')); if (error) return next(new ldap.OperationsError(error.message)); const { resolvedMembers, list } = result; @@ -457,7 +457,7 @@ async function authorizeUserForApp(req, res, next) { const canAccess = apps.canAccess(req.app, req.user); // we return no such object, to avoid leakage of a users existence - if (!canAccess) return next(new ldap.NoSuchObjectError(req.dn.toString())); + if (!canAccess) return next(new ldap.NoSuchObjectError('Invalid user or insufficient previleges')); await eventlog.upsertLoginEvent(req.user.ghost ? eventlog.ACTION_USER_LOGIN_GHOST : eventlog.ACTION_USER_LOGIN, AuditSource.LDAP, { appId: req.app.id, userId: req.user.id, user: users.removePrivateFields(req.user) }); @@ -491,14 +491,14 @@ async function verifyMailboxPassword(mailbox, password) { async function authenticateSftp(req, res, next) { debug('sftp auth: %s (from %s)', req.dn.toString(), req.connection.ldap.id); - if (!req.dn.rdns[0].attrs.cn) return next(new ldap.NoSuchObjectError(req.dn.toString())); + if (!req.dn.rdns[0].attrs.cn) return next(new ldap.NoSuchObjectError('Missing CN')); const email = req.dn.rdns[0].attrs.cn.value.toLowerCase(); const parts = email.split('@'); - if (parts.length !== 2) return next(new ldap.NoSuchObjectError(req.dn.toString())); + if (parts.length !== 2) return next(new ldap.NoSuchObjectError('Invalid CN')); let [error, app] = await safe(apps.getByFqdn(parts[1])); - if (error || !app) return next(new ldap.InvalidCredentialsError(req.dn.toString())); + if (error || !app) return next(new ldap.InvalidCredentialsError()); [error] = await safe(users.verifyWithUsername(parts[0], req.credentials, app.id, { relaxedTotpCheck: true })); if (error) return next(new ldap.InvalidCredentialsError(error.message)); @@ -511,10 +511,10 @@ async function authenticateSftp(req, res, next) { async function userSearchSftp(req, res, next) { debug('sftp user search: dn %s, scope %s, filter %s (from %s)', req.dn.toString(), req.scope, req.filter.toString(), req.connection.ldap.id); - if (req.filter.attribute !== 'username' || !req.filter.value) return next(new ldap.NoSuchObjectError(req.dn.toString())); + if (req.filter.attribute !== 'username' || !req.filter.value) return next(new ldap.NoSuchObjectError()); const parts = req.filter.value.split('@'); - if (parts.length !== 2) return next(new ldap.NoSuchObjectError(req.dn.toString())); + if (parts.length !== 2) return next(new ldap.NoSuchObjectError()); const username = parts[0]; const appFqdn = parts[1]; @@ -610,7 +610,7 @@ async function authenticateService(serviceId, dn, req, res, next) { } async function authenticateMail(req, res, next) { - if (!req.dn.rdns[1].attrs.ou) return next(new ldap.NoSuchObjectError(req.dn.toString())); + if (!req.dn.rdns[1].attrs.ou) return next(new ldap.NoSuchObjectError()); await authenticateService(req.dn.rdns[1].attrs.ou.value.toLowerCase(), req.dn, req, res, next); }