Search in sources :

Example 1 with AuthFailedServiceException

use of com.zimbra.cs.account.AccountServiceException.AuthFailedServiceException in project zm-mailbox by Zimbra.

the class SoapEngine method dispatchRequest.

/**
     * Handles individual requests, either direct or from a batch
     */
Element dispatchRequest(DocumentHandler handler, Element soapReqElem, Map<String, Object> context, ZimbraSoapContext zsc) {
    long startTime = System.currentTimeMillis();
    SoapProtocol soapProto = zsc.getResponseProtocol();
    if (soapReqElem == null) {
        return soapFault(soapProto, "cannot dispatch request", ServiceException.INVALID_REQUEST("no document specified", null));
    }
    if (handler == null) {
        return soapFault(soapProto, "cannot dispatch request", ServiceException.UNKNOWN_DOCUMENT(soapReqElem.getQualifiedName(), null));
    }
    if (RedoLogProvider.getInstance().isSlave() && !handler.isReadOnly()) {
        return soapFault(soapProto, "cannot dispatch request", ServiceException.NON_READONLY_OPERATION_DENIED());
    }
    AuthToken at = zsc.getAuthToken();
    boolean needsAuth = handler.needsAuth(context);
    boolean needsAdminAuth = handler.needsAdminAuth(context);
    if ((needsAuth || needsAdminAuth) && at == null) {
        return soapFault(soapProto, "cannot dispatch request", ServiceException.AUTH_REQUIRED());
    }
    Element response = null;
    SoapTransport.setVia(zsc.getNextVia());
    try {
        Provisioning prov = Provisioning.getInstance();
        if (!prov.getLocalServer().getBooleanAttr(Provisioning.A_zimbraUserServicesEnabled, true) && !(handler instanceof AdminDocumentHandler)) {
            return soapFault(soapProto, "cannot dispatch request", ServiceException.TEMPORARILY_UNAVAILABLE());
        }
        if (needsAdminAuth) {
            AdminAccessControl aac = AdminAccessControl.getAdminAccessControl(at);
            if (!aac.isSufficientAdminForSoap(context, handler)) {
                return soapFault(soapProto, "cannot dispatch request", ServiceException.PERM_DENIED("need adequate admin token"));
            }
        }
        String acctId = null;
        boolean isGuestAccount = true;
        boolean delegatedAuth = false;
        if (at != null) {
            acctId = at.getAccountId();
            isGuestAccount = acctId.equals(GuestAccount.GUID_PUBLIC);
            delegatedAuth = at.isDelegatedAuth();
        }
        if (!isGuestAccount) {
            Account acct = null;
            if (needsAuth || needsAdminAuth) {
                try {
                    acct = AuthProvider.validateAuthToken(prov, at, false);
                } catch (ServiceException e) {
                    return soapFault(soapProto, null, e);
                }
                // also, make sure that the target account (if any) is active
                if (zsc.isDelegatedRequest() && !handler.isAdminCommand()) {
                    Account target = DocumentHandler.getRequestedAccount(zsc);
                    // treat the account as inactive if (a) it doesn't exist, (b) it's in maintenance mode, or (c) we're non-admins and it's not "active"
                    boolean inactive = target == null || Provisioning.ACCOUNT_STATUS_MAINTENANCE.equals(target.getAccountStatus(prov));
                    if (!inactive && (!at.isAdmin() || !AccessManager.getInstance().canAccessAccount(at, target))) {
                        inactive = !target.getAccountStatus(prov).equals(Provisioning.ACCOUNT_STATUS_ACTIVE);
                    }
                    if (inactive) {
                        return soapFault(soapProto, "target account is not active", AccountServiceException.ACCOUNT_INACTIVE(target == null ? zsc.getRequestedAccountId() : target.getName()));
                    }
                }
            }
            // fault in a session for this handler (if necessary) before executing the command
            context.put(ZIMBRA_SESSION, handler.getSession(zsc));
            // try to proxy the request if necessary (don't proxy commands that don't require auth)
            if ((needsAuth || needsAdminAuth) && acct != null) {
                response = handler.proxyIfNecessary(soapReqElem, context);
            }
        }
        // if no proxy, execute the request locally
        if (response == null) {
            if (delegatedAuth) {
                handler.logAuditAccess(at.getAdminAccountId(), acctId, acctId);
            }
            response = handler.handle(soapReqElem, context);
            ZimbraPerf.SOAP_TRACKER.addStat(getStatName(soapReqElem), startTime);
            long duration = System.currentTimeMillis() - startTime;
            if (LC.zimbra_slow_logging_enabled.booleanValue() && duration > LC.zimbra_slow_logging_threshold.longValue() && !soapReqElem.getQName().getName().equals(MailConstants.SYNC_REQUEST.getName())) {
                ZimbraLog.soap.warn("Slow SOAP request (start=" + startTime + "):\n" + soapReqElem.prettyPrint(true));
                ZimbraLog.soap.warn("Slow SOAP response (time=" + duration + "):\n" + response.prettyPrint());
            }
        }
    } catch (SoapFaultException e) {
        response = e.getFault() != null ? e.getFault().detach() : soapProto.soapFault(ServiceException.FAILURE(e.toString(), e));
        if (!e.isSourceLocal()) {
            LOG.debug("handler exception", e);
        }
    } catch (AuthFailedServiceException e) {
        HttpServletRequest httpReq = (HttpServletRequest) context.get(SoapServlet.SERVLET_REQUEST);
        httpReq.setAttribute(ZimbraInvalidLoginFilter.AUTH_FAILED, Boolean.TRUE);
        String clientIp = (String) context.get(SoapEngine.REQUEST_IP);
        httpReq.setAttribute(SoapEngine.REQUEST_IP, clientIp);
        response = soapProto.soapFault(e);
        if (LOG.isDebugEnabled()) {
            LOG.debug("handler exception: %s%s", e.getMessage(), e.getReason(", %s"), e);
        } else {
            // Don't log stack trace for auth failures, since they commonly happen
            LOG.info("handler exception: %s%s", e.getMessage(), e.getReason(", %s"));
        }
    } catch (ServiceException e) {
        response = soapFault(soapProto, "handler exception", e);
    // XXX: if the session was new, do we want to delete it?
    } catch (Throwable e) {
        // don't interfere with Jetty Continuations -- pass the exception on up
        if (e.getClass().getName().equals("org.eclipse.jetty.continuation.ContinuationThrowable")) {
            throw (Error) e;
        }
        // TODO: better exception stack traces during develope?
        response = soapProto.soapFault(ServiceException.FAILURE(e.toString(), e));
        if (e instanceof OutOfMemoryError) {
            Zimbra.halt("handler exception", e);
        }
        LOG.warn("handler exception", e);
    // XXX: if the session was new, do we want to delete it?
    } finally {
        SoapTransport.clearVia();
    }
    return response;
}
Also used : GuestAccount(com.zimbra.cs.account.GuestAccount) Account(com.zimbra.cs.account.Account) AuthFailedServiceException(com.zimbra.cs.account.AccountServiceException.AuthFailedServiceException) Element(com.zimbra.common.soap.Element) SoapProtocol(com.zimbra.common.soap.SoapProtocol) AdminAccessControl(com.zimbra.cs.service.admin.AdminAccessControl) Provisioning(com.zimbra.cs.account.Provisioning) SoapFaultException(com.zimbra.common.soap.SoapFaultException) HttpServletRequest(javax.servlet.http.HttpServletRequest) AccountServiceException(com.zimbra.cs.account.AccountServiceException) ServiceException(com.zimbra.common.service.ServiceException) AuthFailedServiceException(com.zimbra.cs.account.AccountServiceException.AuthFailedServiceException) AdminDocumentHandler(com.zimbra.cs.service.admin.AdminDocumentHandler) AuthToken(com.zimbra.cs.account.AuthToken)

Example 2 with AuthFailedServiceException

use of com.zimbra.cs.account.AccountServiceException.AuthFailedServiceException in project zm-mailbox by Zimbra.

the class PreAuthServlet method doGet.

@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    ZimbraLog.clearContext();
    try {
        Provisioning prov = Provisioning.getInstance();
        Server server = prov.getLocalServer();
        String referMode = server.getAttr(Provisioning.A_zimbraMailReferMode, "wronghost");
        boolean isRedirect = getOptionalParam(req, PARAM_ISREDIRECT, "0").equals("1");
        String rawAuthToken = getOptionalParam(req, PARAM_AUTHTOKEN, null);
        AuthToken authToken = null;
        if (rawAuthToken != null) {
            authToken = AuthProvider.getAuthToken(rawAuthToken);
            if (authToken == null) {
                throw new AuthTokenException("unable to get auth token from " + PARAM_AUTHTOKEN);
            } else if (authToken.isExpired()) {
                throw new AuthTokenException("auth token expired");
            } else if (!authToken.isRegistered()) {
                throw new AuthTokenException("authtoken is invalid");
            }
        }
        if (rawAuthToken != null) {
            if (!authToken.isRegistered()) {
                throw new AuthTokenException("authtoken is not registered");
            }
            if (authToken.isExpired()) {
                throw new AuthTokenException("authtoken is expired registered");
            }
            // we've got an auth token in the request:
            // See if we need a redirect to the correct server
            boolean isAdmin = authToken != null && AuthToken.isAnyAdmin(authToken);
            Account acct = prov.get(AccountBy.id, authToken.getAccountId(), authToken);
            if (isAdmin || !needReferral(acct, referMode, isRedirect)) {
                //authtoken in get request is for one time use only. Deregister and generate new one.
                if (authToken instanceof ZimbraAuthToken) {
                    ZimbraAuthToken oneTimeToken = (ZimbraAuthToken) authToken;
                    ZimbraAuthToken newZimbraAuthToken = null;
                    try {
                        newZimbraAuthToken = oneTimeToken.clone();
                    } catch (CloneNotSupportedException e) {
                        throw new ServletException(e);
                    }
                    newZimbraAuthToken.resetTokenId();
                    oneTimeToken.deRegister();
                    authToken = newZimbraAuthToken;
                    ZimbraLog.account.debug("Deregistered the one time preauth token and issuing new one to the user.");
                }
                // no need to redirect to the correct server, just send them off to do business
                setCookieAndRedirect(req, resp, authToken);
            } else {
                // redirect to the correct server with the incoming auth token
                // we no longer send the auth token we generate over when we redirect to the correct server,
                // but customer can be sending a token in their preauth URL, in this case, just
                // send over the auth token as is.
                redirectToCorrectServer(req, resp, acct, rawAuthToken);
            }
        } else {
            // no auth token in the request URL.  See if we should redirect this request
            // to the correct server, or should do the preauth locally.
            String preAuth = getRequiredParam(req, resp, PARAM_PREAUTH);
            String account = getRequiredParam(req, resp, PARAM_ACCOUNT);
            String accountBy = getOptionalParam(req, PARAM_BY, AccountBy.name.name());
            AccountBy by = AccountBy.fromString(accountBy);
            boolean admin = getOptionalParam(req, PARAM_ADMIN, "0").equals("1") && isAdminRequest(req);
            long timestamp = Long.parseLong(getRequiredParam(req, resp, PARAM_TIMESTAMP));
            long expires = Long.parseLong(getRequiredParam(req, resp, PARAM_EXPIRES));
            Account acct = null;
            acct = prov.get(by, account, authToken);
            Map<String, Object> authCtxt = new HashMap<String, Object>();
            authCtxt.put(AuthContext.AC_ORIGINATING_CLIENT_IP, ZimbraServlet.getOrigIp(req));
            authCtxt.put(AuthContext.AC_REMOTE_IP, ZimbraServlet.getClientIp(req));
            authCtxt.put(AuthContext.AC_ACCOUNT_NAME_PASSEDIN, account);
            authCtxt.put(AuthContext.AC_USER_AGENT, req.getHeader("User-Agent"));
            boolean acctAutoProvisioned = false;
            if (acct == null) {
                //
                if (by == AccountBy.name && !admin) {
                    try {
                        EmailAddress email = new EmailAddress(account, false);
                        String domainName = email.getDomain();
                        Domain domain = domainName == null ? null : prov.get(Key.DomainBy.name, domainName);
                        prov.preAuthAccount(domain, account, accountBy, timestamp, expires, preAuth, authCtxt);
                        acct = prov.autoProvAccountLazy(domain, account, null, AutoProvAuthMech.PREAUTH);
                        if (acct != null) {
                            acctAutoProvisioned = true;
                        }
                    } catch (AuthFailedServiceException e) {
                        ZimbraLog.account.debug("auth failed, unable to auto provisioing acct " + account, e);
                    } catch (ServiceException e) {
                        ZimbraLog.account.info("unable to auto provisioing acct " + account, e);
                    }
                }
            }
            if (acct == null) {
                throw AuthFailedServiceException.AUTH_FAILED(account, account, "account not found");
            }
            String accountStatus = acct.getAccountStatus(prov);
            if (!Provisioning.ACCOUNT_STATUS_ACTIVE.equalsIgnoreCase(accountStatus)) {
                if (Provisioning.ACCOUNT_STATUS_MAINTENANCE.equalsIgnoreCase(accountStatus)) {
                    throw AccountServiceException.MAINTENANCE_MODE();
                } else {
                    throw AccountServiceException.ACCOUNT_INACTIVE(acct.getName());
                }
            }
            if (admin) {
                boolean isDomainAdminAccount = acct.getBooleanAttr(Provisioning.A_zimbraIsDomainAdminAccount, false);
                boolean isAdminAccount = acct.getBooleanAttr(Provisioning.A_zimbraIsAdminAccount, false);
                boolean isDelegatedAdminAccount = acct.getBooleanAttr(Provisioning.A_zimbraIsDelegatedAdminAccount, false);
                boolean ok = (isDomainAdminAccount || isAdminAccount || isDelegatedAdminAccount);
                if (!ok)
                    throw ServiceException.PERM_DENIED("not an admin account");
            }
            if (admin || !needReferral(acct, referMode, isRedirect)) {
                // do preauth locally
                if (!acctAutoProvisioned) {
                    prov.preAuthAccount(acct, account, accountBy, timestamp, expires, preAuth, admin, authCtxt);
                }
                AuthToken at;
                if (admin)
                    at = (expires == 0) ? AuthProvider.getAuthToken(acct, admin) : AuthProvider.getAuthToken(acct, expires, admin, null);
                else
                    at = (expires == 0) ? AuthProvider.getAuthToken(acct) : AuthProvider.getAuthToken(acct, expires);
                setCookieAndRedirect(req, resp, at);
            } else {
                // redirect to the correct server.
                // Note: we do not send over the generated auth token (the auth token param passed to
                // redirectToCorrectServer is null).
                redirectToCorrectServer(req, resp, acct, null);
            }
        }
    } catch (ServiceException e) {
        resp.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage());
    } catch (AuthTokenException e) {
        resp.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage());
    }
}
Also used : Account(com.zimbra.cs.account.Account) Server(com.zimbra.cs.account.Server) AuthFailedServiceException(com.zimbra.cs.account.AccountServiceException.AuthFailedServiceException) HashMap(java.util.HashMap) ZimbraAuthToken(com.zimbra.cs.account.ZimbraAuthToken) Provisioning(com.zimbra.cs.account.Provisioning) EmailAddress(com.zimbra.cs.account.names.NameUtil.EmailAddress) AccountBy(com.zimbra.common.account.Key.AccountBy) ServletException(javax.servlet.ServletException) AccountServiceException(com.zimbra.cs.account.AccountServiceException) ServiceException(com.zimbra.common.service.ServiceException) AuthFailedServiceException(com.zimbra.cs.account.AccountServiceException.AuthFailedServiceException) AuthTokenException(com.zimbra.cs.account.AuthTokenException) ZimbraAuthToken(com.zimbra.cs.account.ZimbraAuthToken) AuthToken(com.zimbra.cs.account.AuthToken) Domain(com.zimbra.cs.account.Domain)

Example 3 with AuthFailedServiceException

use of com.zimbra.cs.account.AccountServiceException.AuthFailedServiceException in project zm-mailbox by Zimbra.

the class SpnegoAuthServlet method doGet.

@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    ZimbraLog.clearContext();
    addRemoteIpToLoggingContext(req);
    addUAToLoggingContext(req);
    boolean isAdminRequest = false;
    boolean isFromZCO = false;
    try {
        isAdminRequest = isOnAdminPort(req);
        isFromZCO = isFromZCO(req);
        Principal principal = req.getUserPrincipal();
        if (principal == null) {
            throw AuthFailedServiceException.AUTH_FAILED("no principal");
        }
        if (!(principal instanceof ZimbraPrincipal)) {
            throw AuthFailedServiceException.AUTH_FAILED(principal.getName(), "not ZimbraPrincipal", (Throwable) null);
        }
        ZimbraPrincipal zimbraPrincipal = (ZimbraPrincipal) principal;
        AuthToken authToken = authorize(req, AuthContext.Protocol.spnego, zimbraPrincipal, isAdminRequest);
        if (isFromZCO) {
            setAuthTokenCookieAndReturn(req, resp, authToken);
        } else {
            setAuthTokenCookieAndRedirect(req, resp, zimbraPrincipal.getAccount(), authToken);
        }
    } catch (ServiceException e) {
        if (e instanceof AuthFailedServiceException) {
            AuthFailedServiceException afe = (AuthFailedServiceException) e;
            ZimbraLog.account.info("spnego auth failed: " + afe.getMessage() + afe.getReason(", %s"));
        } else {
            ZimbraLog.account.info("spnego auth failed: " + e.getMessage());
        }
        ZimbraLog.account.debug("spnego auth failed", e);
        if (isFromZCO) {
            resp.sendError(HttpServletResponse.SC_FORBIDDEN, e.getMessage());
        } else {
            try {
                redirectToErrorPage(req, resp, isAdminRequest, Provisioning.getInstance().getConfig().getSpnegoAuthErrorURL());
            } catch (ServiceException se) {
                ZimbraLog.account.info("failed to redirect to error page: " + se.getMessage());
                resp.sendError(HttpServletResponse.SC_FORBIDDEN, e.getMessage());
            }
        }
    }
}
Also used : AuthFailedServiceException(com.zimbra.cs.account.AccountServiceException.AuthFailedServiceException) ServiceException(com.zimbra.common.service.ServiceException) AuthFailedServiceException(com.zimbra.cs.account.AccountServiceException.AuthFailedServiceException) ZimbraPrincipal(com.zimbra.cs.service.authenticator.SSOAuthenticator.ZimbraPrincipal) AuthToken(com.zimbra.cs.account.AuthToken) ZimbraPrincipal(com.zimbra.cs.service.authenticator.SSOAuthenticator.ZimbraPrincipal) Principal(java.security.Principal)

Example 4 with AuthFailedServiceException

use of com.zimbra.cs.account.AccountServiceException.AuthFailedServiceException in project zm-mailbox by Zimbra.

the class Auth method handle.

@Override
public Element handle(Element request, Map<String, Object> context) throws ServiceException {
    ZimbraSoapContext zsc = getZimbraSoapContext(context);
    Provisioning prov = Provisioning.getInstance();
    // Look up the specified account.  It is optional in the <authToken> case.
    String acctValuePassedIn = null, acctValue = null, acctByStr = null;
    AccountBy acctBy = null;
    Account acct = null;
    Element acctEl = request.getOptionalElement(AccountConstants.E_ACCOUNT);
    boolean csrfSupport = request.getAttributeBool(AccountConstants.A_CSRF_SUPPORT, false);
    if (acctEl != null) {
        acctValuePassedIn = acctEl.getText();
        acctValue = acctValuePassedIn;
        acctByStr = acctEl.getAttribute(AccountConstants.A_BY, AccountBy.name.name());
        acctBy = AccountBy.fromString(acctByStr);
        if (acctBy == AccountBy.name) {
            Element virtualHostEl = request.getOptionalElement(AccountConstants.E_VIRTUAL_HOST);
            String virtualHost = virtualHostEl == null ? null : virtualHostEl.getText().toLowerCase();
            if (virtualHost != null && acctValue.indexOf('@') == -1) {
                Domain d = prov.get(Key.DomainBy.virtualHostname, virtualHost);
                if (d != null)
                    acctValue = acctValue + "@" + d.getName();
            }
        }
        acct = prov.get(acctBy, acctValue);
    }
    TrustedDeviceToken trustedToken = null;
    if (acct != null) {
        TrustedDevices trustedDeviceManager = TwoFactorAuth.getFactory().getTrustedDevices(acct);
        if (trustedDeviceManager != null) {
            trustedToken = trustedDeviceManager.getTokenFromRequest(request, context);
            if (trustedToken != null && trustedToken.isExpired()) {
                TrustedDevice device = trustedDeviceManager.getTrustedDeviceByTrustedToken(trustedToken);
                if (device != null) {
                    device.revoke();
                }
            }
        }
    }
    String password = request.getAttribute(AccountConstants.E_PASSWORD, null);
    boolean generateDeviceId = request.getAttributeBool(AccountConstants.A_GENERATE_DEVICE_ID, false);
    String twoFactorCode = request.getAttribute(AccountConstants.E_TWO_FACTOR_CODE, null);
    String newDeviceId = generateDeviceId ? UUIDUtil.generateUUID() : null;
    Element authTokenEl = request.getOptionalElement(AccountConstants.E_AUTH_TOKEN);
    if (authTokenEl != null) {
        boolean verifyAccount = authTokenEl.getAttributeBool(AccountConstants.A_VERIFY_ACCOUNT, false);
        if (verifyAccount && acctEl == null) {
            throw ServiceException.INVALID_REQUEST("missing required element: " + AccountConstants.E_ACCOUNT, null);
        }
        try {
            AuthToken at = AuthProvider.getAuthToken(authTokenEl, acct);
            addAccountToLogContextByAuthToken(prov, at);
            // so the account will show in log context
            if (!checkPasswordSecurity(context))
                throw ServiceException.INVALID_REQUEST("clear text password is not allowed", null);
            AuthToken.Usage usage = at.getUsage();
            if (usage != Usage.AUTH && usage != Usage.TWO_FACTOR_AUTH) {
                throw AuthFailedServiceException.AUTH_FAILED("invalid auth token");
            }
            Account authTokenAcct = AuthProvider.validateAuthToken(prov, at, false, usage);
            if (verifyAccount) {
                // can treat the auth token as an opaque string.
                if (acct == null || !acct.getId().equalsIgnoreCase(authTokenAcct.getId())) {
                    throw new AuthTokenException("auth token doesn't match the named account");
                }
            }
            if (usage == Usage.AUTH) {
                ServletRequest httpReq = (ServletRequest) context.get(SoapServlet.SERVLET_REQUEST);
                httpReq.setAttribute(CsrfFilter.AUTH_TOKEN, at);
                if (csrfSupport && !at.isCsrfTokenEnabled()) {
                    // handle case where auth token was originally generated with csrf support
                    // and now client sends the same auth token but saying csrfSupport is turned off
                    // in that case do not disable CSRF check for this authToken.
                    at.setCsrfTokenEnabled(csrfSupport);
                }
                return doResponse(request, at, zsc, context, authTokenAcct, csrfSupport, trustedToken, newDeviceId);
            } else {
                acct = authTokenAcct;
            }
        } catch (AuthTokenException e) {
            throw ServiceException.AUTH_REQUIRED();
        }
    }
    if (!checkPasswordSecurity(context)) {
        throw ServiceException.INVALID_REQUEST("clear text password is not allowed", null);
    }
    Element preAuthEl = request.getOptionalElement(AccountConstants.E_PREAUTH);
    String deviceId = request.getAttribute(AccountConstants.E_DEVICE_ID, null);
    long expires = 0;
    Map<String, Object> authCtxt = new HashMap<String, Object>();
    authCtxt.put(AuthContext.AC_ORIGINATING_CLIENT_IP, context.get(SoapEngine.ORIG_REQUEST_IP));
    authCtxt.put(AuthContext.AC_REMOTE_IP, context.get(SoapEngine.SOAP_REQUEST_IP));
    authCtxt.put(AuthContext.AC_ACCOUNT_NAME_PASSEDIN, acctValuePassedIn);
    authCtxt.put(AuthContext.AC_USER_AGENT, zsc.getUserAgent());
    boolean acctAutoProvisioned = false;
    if (acct == null) {
        // try LAZY auto provision if it is enabled
        if (acctBy == AccountBy.name || acctBy == AccountBy.krb5Principal) {
            try {
                if (acctBy == AccountBy.name) {
                    EmailAddress email = new EmailAddress(acctValue, false);
                    String domainName = email.getDomain();
                    Domain domain = domainName == null ? null : prov.get(Key.DomainBy.name, domainName);
                    if (password != null) {
                        acct = prov.autoProvAccountLazy(domain, acctValuePassedIn, password, null);
                    } else if (preAuthEl != null) {
                        long timestamp = preAuthEl.getAttributeLong(AccountConstants.A_TIMESTAMP);
                        expires = preAuthEl.getAttributeLong(AccountConstants.A_EXPIRES, 0);
                        String preAuth = preAuthEl.getTextTrim();
                        prov.preAuthAccount(domain, acctValue, acctByStr, timestamp, expires, preAuth, authCtxt);
                        acct = prov.autoProvAccountLazy(domain, acctValuePassedIn, null, AutoProvAuthMech.PREAUTH);
                    }
                } else {
                    if (password != null) {
                        Domain domain = Krb5Principal.getDomainByKrb5Principal(acctValuePassedIn);
                        if (domain != null) {
                            acct = prov.autoProvAccountLazy(domain, acctValuePassedIn, password, null);
                        }
                    }
                }
                if (acct != null) {
                    acctAutoProvisioned = true;
                }
            } catch (AuthFailedServiceException e) {
                ZimbraLog.account.debug("auth failed, unable to auto provisioing acct " + acctValue, e);
            } catch (ServiceException e) {
                ZimbraLog.account.info("unable to auto provisioing acct " + acctValue, e);
            }
        }
    }
    if (acct == null) {
        // try ZMG Proxy auto provision if it is enabled
        if (acctBy == AccountBy.name && password != null) {
            Pair<Account, Boolean> result = null;
            try {
                result = prov.autoProvZMGProxyAccount(acctValuePassedIn, password);
            } catch (AuthFailedServiceException e) {
            // Most likely in error with user creds
            } catch (ServiceException e) {
                ZimbraLog.account.info("unable to auto provision acct " + acctValuePassedIn, e);
            }
            if (result != null) {
                acct = result.getFirst();
                acctAutoProvisioned = result.getSecond();
            }
        }
    }
    if (acct == null) {
        throw AuthFailedServiceException.AUTH_FAILED(acctValue, acctValuePassedIn, "account not found");
    }
    AccountUtil.addAccountToLogContext(prov, acct.getId(), ZimbraLog.C_NAME, ZimbraLog.C_ID, null);
    Boolean registerTrustedDevice = false;
    TwoFactorAuth twoFactorManager = TwoFactorAuth.getFactory().getTwoFactorAuth(acct);
    if (twoFactorManager.twoFactorAuthEnabled()) {
        registerTrustedDevice = trustedToken == null && request.getAttributeBool(AccountConstants.A_TRUSTED_DEVICE, false);
    }
    // if account was auto provisioned, we had already authenticated the principal
    if (!acctAutoProvisioned) {
        boolean trustedDeviceOverride = false;
        if (trustedToken != null && acct.isFeatureTrustedDevicesEnabled()) {
            if (trustedToken.isExpired()) {
                ZimbraLog.account.debug("trusted token is expired");
                registerTrustedDevice = false;
            } else {
                Map<String, Object> attrs = getTrustedDeviceAttrs(zsc, deviceId);
                try {
                    verifyTrustedDevice(acct, trustedToken, attrs);
                    trustedDeviceOverride = true;
                } catch (AuthFailedServiceException e) {
                    ZimbraLog.account.info("trusted device not verified");
                }
            }
        }
        boolean usingTwoFactorAuth = acct != null && twoFactorManager.twoFactorAuthRequired() && !trustedDeviceOverride;
        boolean twoFactorAuthWithToken = usingTwoFactorAuth && authTokenEl != null;
        if (password != null || twoFactorAuthWithToken) {
            // authentication logic can be reached with either a password, or a 2FA auth token
            if (usingTwoFactorAuth && twoFactorCode == null && password != null) {
                int mtaAuthPort = acct.getServer().getMtaAuthPort();
                boolean supportsAppSpecificPaswords = acct.isFeatureAppSpecificPasswordsEnabled() && zsc.getPort() == mtaAuthPort;
                if (supportsAppSpecificPaswords && password != null) {
                    // if we are here, it means we are authenticating SMTP,
                    // so app-specific passwords are accepted. Other protocols (pop, imap)
                    // doesn't touch this code, so their authentication happens in ZimbraAuth.
                    AppSpecificPasswords appPasswords = TwoFactorAuth.getFactory().getAppSpecificPasswords(acct, acctValuePassedIn);
                    appPasswords.authenticate(password);
                } else {
                    prov.authAccount(acct, password, AuthContext.Protocol.soap, authCtxt);
                    return needTwoFactorAuth(acct, twoFactorManager, zsc);
                }
            } else {
                if (password != null) {
                    prov.authAccount(acct, password, AuthContext.Protocol.soap, authCtxt);
                } else {
                    // it's ok to not have a password if the client is using a 2FA auth token for the 2nd step of 2FA
                    if (!twoFactorAuthWithToken) {
                        throw ServiceException.AUTH_REQUIRED();
                    }
                }
                if (usingTwoFactorAuth) {
                    // check that 2FA has been enabled, in case the client is passing in a twoFactorCode prior to setting up 2FA
                    if (!twoFactorManager.twoFactorAuthEnabled()) {
                        throw AccountServiceException.TWO_FACTOR_SETUP_REQUIRED();
                    }
                    AuthToken twoFactorToken = null;
                    if (password == null) {
                        try {
                            twoFactorToken = AuthProvider.getAuthToken(authTokenEl, acct);
                            Account twoFactorTokenAcct = AuthProvider.validateAuthToken(prov, twoFactorToken, false, Usage.TWO_FACTOR_AUTH);
                            boolean verifyAccount = authTokenEl.getAttributeBool(AccountConstants.A_VERIFY_ACCOUNT, false);
                            if (verifyAccount && !twoFactorTokenAcct.getId().equalsIgnoreCase(acct.getId())) {
                                throw new AuthTokenException("two-factor auth token doesn't match the named account");
                            }
                        } catch (AuthTokenException e) {
                            throw AuthFailedServiceException.AUTH_FAILED("bad auth token");
                        }
                    }
                    TwoFactorAuth manager = TwoFactorAuth.getFactory().getTwoFactorAuth(acct);
                    if (twoFactorCode != null) {
                        manager.authenticate(twoFactorCode);
                    } else {
                        throw AuthFailedServiceException.AUTH_FAILED("no two-factor code provided");
                    }
                    if (twoFactorToken != null) {
                        try {
                            twoFactorToken.deRegister();
                        } catch (AuthTokenException e) {
                            throw ServiceException.FAILURE("cannot de-register two-factor auth token", e);
                        }
                    }
                }
            }
        } else if (preAuthEl != null) {
            long timestamp = preAuthEl.getAttributeLong(AccountConstants.A_TIMESTAMP);
            expires = preAuthEl.getAttributeLong(AccountConstants.A_EXPIRES, 0);
            String preAuth = preAuthEl.getTextTrim();
            prov.preAuthAccount(acct, acctValue, acctByStr, timestamp, expires, preAuth, authCtxt);
        } else {
            throw ServiceException.INVALID_REQUEST("must specify " + AccountConstants.E_PASSWORD, null);
        }
    }
    AuthToken at = expires == 0 ? AuthProvider.getAuthToken(acct) : AuthProvider.getAuthToken(acct, expires);
    if (registerTrustedDevice && (trustedToken == null || trustedToken.isExpired())) {
        //generate a new trusted device token if there is no existing one or if the current one is no longer valid
        Map<String, Object> attrs = getTrustedDeviceAttrs(zsc, newDeviceId == null ? deviceId : newDeviceId);
        TrustedDevices trustedDeviceManager = TwoFactorAuth.getFactory().getTrustedDevices(acct);
        trustedToken = trustedDeviceManager.registerTrustedDevice(attrs);
    }
    ServletRequest httpReq = (ServletRequest) context.get(SoapServlet.SERVLET_REQUEST);
    // For CSRF filter so that token generation can happen
    if (csrfSupport && !at.isCsrfTokenEnabled()) {
        // handle case where auth token was originally generated with csrf support
        // and now client sends the same auth token but saying csrfSupport is turned off
        // in that case do not disable CSRF check for this authToken.
        at.setCsrfTokenEnabled(csrfSupport);
    }
    httpReq.setAttribute(CsrfFilter.AUTH_TOKEN, at);
    return doResponse(request, at, zsc, context, acct, csrfSupport, trustedToken, newDeviceId);
}
Also used : Account(com.zimbra.cs.account.Account) HttpServletRequest(javax.servlet.http.HttpServletRequest) ServletRequest(javax.servlet.ServletRequest) AuthFailedServiceException(com.zimbra.cs.account.AccountServiceException.AuthFailedServiceException) TrustedDeviceToken(com.zimbra.cs.account.TrustedDeviceToken) HashMap(java.util.HashMap) Element(com.zimbra.common.soap.Element) Provisioning(com.zimbra.cs.account.Provisioning) AccountBy(com.zimbra.common.account.Key.AccountBy) TwoFactorAuth(com.zimbra.cs.account.auth.twofactor.TwoFactorAuth) Usage(com.zimbra.cs.account.AuthToken.Usage) AppSpecificPasswords(com.zimbra.cs.account.auth.twofactor.AppSpecificPasswords) EmailAddress(com.zimbra.cs.account.names.NameUtil.EmailAddress) AccountServiceException(com.zimbra.cs.account.AccountServiceException) ServiceException(com.zimbra.common.service.ServiceException) AuthFailedServiceException(com.zimbra.cs.account.AccountServiceException.AuthFailedServiceException) ZimbraSoapContext(com.zimbra.soap.ZimbraSoapContext) AuthTokenException(com.zimbra.cs.account.AuthTokenException) TrustedDevice(com.zimbra.cs.account.TrustedDevice) AuthToken(com.zimbra.cs.account.AuthToken) Domain(com.zimbra.cs.account.Domain) TrustedDevices(com.zimbra.cs.account.auth.twofactor.TrustedDevices)

Example 5 with AuthFailedServiceException

use of com.zimbra.cs.account.AccountServiceException.AuthFailedServiceException in project zm-mailbox by Zimbra.

the class CertAuthServlet method doGet.

@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    ZimbraLog.clearContext();
    addRemoteIpToLoggingContext(req);
    addUAToLoggingContext(req);
    String url = req.getRequestURI();
    Matcher matcher = allowedUrl.matcher(url);
    boolean isAdminRequest = false;
    if (!matcher.matches()) {
        String msg = "resource not allowed on the certauth servlet: " + url;
        ZimbraLog.account.error(msg);
        sendback403Message(req, resp, msg);
        return;
    } else {
        if (matcher.groupCount() > 3 && "admin".equals(matcher.group(3))) {
            isAdminRequest = true;
        }
    }
    try {
        SSOAuthenticator authenticator = new ClientCertAuthenticator(req, resp);
        ZimbraPrincipal principal = null;
        principal = authenticator.authenticate();
        AuthToken authToken = authorize(req, AuthContext.Protocol.client_certificate, principal, isAdminRequest);
        setAuthTokenCookieAndRedirect(req, resp, principal.getAccount(), authToken);
        return;
    } catch (ServiceException e) {
        String reason = "";
        if (e instanceof AuthFailedServiceException) {
            reason = ((AuthFailedServiceException) e).getReason(", %s");
        }
        ZimbraLog.account.debug("client certificate auth failed: " + e.getMessage() + reason, e);
        dispatchOnError(req, resp, isAdminRequest, e.getMessage());
    }
}
Also used : ClientCertAuthenticator(com.zimbra.cs.service.authenticator.ClientCertAuthenticator) AuthFailedServiceException(com.zimbra.cs.account.AccountServiceException.AuthFailedServiceException) ServiceException(com.zimbra.common.service.ServiceException) SSOAuthenticatorServiceException(com.zimbra.cs.service.authenticator.SSOAuthenticator.SSOAuthenticatorServiceException) AuthFailedServiceException(com.zimbra.cs.account.AccountServiceException.AuthFailedServiceException) Matcher(java.util.regex.Matcher) SSOAuthenticator(com.zimbra.cs.service.authenticator.SSOAuthenticator) ZimbraPrincipal(com.zimbra.cs.service.authenticator.SSOAuthenticator.ZimbraPrincipal) AuthToken(com.zimbra.cs.account.AuthToken)

Aggregations

ServiceException (com.zimbra.common.service.ServiceException)7 AuthFailedServiceException (com.zimbra.cs.account.AccountServiceException.AuthFailedServiceException)7 AuthToken (com.zimbra.cs.account.AuthToken)5 Account (com.zimbra.cs.account.Account)4 Provisioning (com.zimbra.cs.account.Provisioning)4 AccountServiceException (com.zimbra.cs.account.AccountServiceException)3 HttpServletRequest (javax.servlet.http.HttpServletRequest)3 AccountBy (com.zimbra.common.account.Key.AccountBy)2 Element (com.zimbra.common.soap.Element)2 AuthTokenException (com.zimbra.cs.account.AuthTokenException)2 Domain (com.zimbra.cs.account.Domain)2 EmailAddress (com.zimbra.cs.account.names.NameUtil.EmailAddress)2 SSOAuthenticatorServiceException (com.zimbra.cs.service.authenticator.SSOAuthenticator.SSOAuthenticatorServiceException)2 ZimbraPrincipal (com.zimbra.cs.service.authenticator.SSOAuthenticator.ZimbraPrincipal)2 HashMap (java.util.HashMap)2 SoapFaultException (com.zimbra.common.soap.SoapFaultException)1 SoapProtocol (com.zimbra.common.soap.SoapProtocol)1 Usage (com.zimbra.cs.account.AuthToken.Usage)1 CacheAwareProvisioning (com.zimbra.cs.account.CacheAwareProvisioning)1 GuestAccount (com.zimbra.cs.account.GuestAccount)1