diff --git a/CHANGES b/CHANGES index a7e45415b..0a76aefaf 100644 --- a/CHANGES +++ b/CHANGES @@ -2146,4 +2146,5 @@ * Disable THP * filemanager: allow download dirs as zip files * aws: add china region +* security: fix issue where apps could send with any username (but valid password) diff --git a/src/ldap.js b/src/ldap.js index 38c57c211..faf229bd9 100644 --- a/src/ldap.js +++ b/src/ldap.js @@ -634,16 +634,35 @@ function userSearchSftp(req, res, next) { }); } +function verifyAppMailboxPassword(addonId, username, password, callback) { + assert.strictEqual(typeof addonId, 'string'); + assert.strictEqual(typeof username, 'string'); + assert.strictEqual(typeof password, 'string'); + assert.strictEqual(typeof callback, 'function'); + + const pattern = addonId === 'sendmail' ? 'MAIL_SMTP' : 'MAIL_IMAP'; + appdb.getAppIdByAddonConfigValue(addonId, `%${pattern}_PASSWORD`, password, function (error, appId) { // search by password because this is unique for each app + if (error) return callback(error); + + appdb.getAddonConfig(appId, addonId, function (error, result) { + if (error) return callback(error); + + if (!result.some(r => r.name.endsWith(`${pattern}_USERNAME`) && r.value === username)) return callback(new BoxError(BoxError.INVALID_CREDENTIALS)); + + callback(null); + }); + }); +} + function authenticateMailAddon(req, res, next) { debug('mail addon 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())); - var email = req.dn.rdns[0].attrs.cn.value.toLowerCase(); var parts = email.split('@'); if (parts.length !== 2) return next(new ldap.NoSuchObjectError(req.dn.toString())); const addonId = req.dn.rdns[1].attrs.ou.value.toLowerCase(); // 'sendmail' or 'recvmail' + if (addonId !== 'sendmail' && addonId !== 'recvmail') return next(new ldap.OperationsError('Invalid DN')); mail.getDomain(parts[1], function (error, domain) { if (error && error.reason === BoxError.NOT_FOUND) return next(new ldap.NoSuchObjectError(req.dn.toString())); @@ -651,15 +670,11 @@ function authenticateMailAddon(req, res, next) { if (addonId === 'recvmail' && !domain.enabled) return next(new ldap.NoSuchObjectError(req.dn.toString())); - let namePattern; // manifest v2 has a CLOUDRON_ prefix for names - if (addonId === 'sendmail') namePattern = '%MAIL_SMTP_PASSWORD'; - else if (addonId === 'recvmail') namePattern = '%MAIL_IMAP_PASSWORD'; - else return next(new ldap.OperationsError('Invalid DN')); + verifyAppMailboxPassword(addonId, email, req.credentials || '', function (error) { + if (!error) return res.end(); // validated as app - // note: with sendmail addon, apps can send mail without a mailbox (unlike users) - appdb.getAppIdByAddonConfigValue(addonId, namePattern, req.credentials || '', function (error, appId) { + if (error && error.reason === BoxError.INVALID_CREDENTIALS) return next(new ldap.InvalidCredentialsError(req.dn.toString())); if (error && error.reason !== BoxError.NOT_FOUND) return next(new ldap.OperationsError(error.message)); - if (appId) return res.end(); mailboxdb.getMailbox(parts[0], parts[1], function (error, mailbox) { if (error && error.reason === BoxError.NOT_FOUND) return next(new ldap.NoSuchObjectError(req.dn.toString())); diff --git a/src/test/ldap-test.js b/src/test/ldap-test.js index f2788d96c..b7d041745 100644 --- a/src/test/ldap-test.js +++ b/src/test/ldap-test.js @@ -110,8 +110,8 @@ function setup(done) { (done) => mailboxdb.addMailbox(USER_0.username.toLowerCase(), DOMAIN_0.domain, USER_0.id, mail.OWNERTYPE_USER, done), (done) => mailboxdb.setAliasesForName(USER_0.username.toLowerCase(), DOMAIN_0.domain, [ { name: USER_0_ALIAS.toLocaleLowerCase(), domain: DOMAIN_0.domain} ], done), appdb.update.bind(null, APP_0.id, { containerId: APP_0.containerId }), - appdb.setAddonConfig.bind(null, APP_0.id, 'sendmail', [{ name: 'MAIL_SMTP_PASSWORD', value : 'sendmailpassword' }]), - appdb.setAddonConfig.bind(null, APP_0.id, 'recvmail', [{ name: 'MAIL_IMAP_PASSWORD', value : 'recvmailpassword' }]), + appdb.setAddonConfig.bind(null, APP_0.id, 'sendmail', [{ name: 'MAIL_SMTP_USERNAME', value : `${APP_0.location}.app@${DOMAIN_0.domain}` }, { name: 'MAIL_SMTP_PASSWORD', value : 'sendmailpassword' }]), + appdb.setAddonConfig.bind(null, APP_0.id, 'recvmail', [{ name: 'MAIL_IMAP_USERNAME', value : `${APP_0.location}.app@${DOMAIN_0.domain}` }, { name: 'MAIL_IMAP_PASSWORD', value : 'recvmailpassword' }]), mailboxdb.addMailbox.bind(null, APP_0.location + '.app', APP_0.domain, APP_0.id, mail.OWNERTYPE_USER), function (callback) {