All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.mobicents.servlet.sip.undertow.security.SipSecurityUtils Maven / Gradle / Ivy

/*
 * TeleStax, Open Source Cloud Communications
 * Copyright 2011-2015, Telestax Inc and individual contributors
 * by the @authors tag.
 *
 * This program is free software: you can redistribute it and/or modify
 * under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation; either version 3 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see 
 */
package org.mobicents.servlet.sip.undertow.security;

import java.lang.reflect.Field;
import java.security.Principal;
import java.util.concurrent.ConcurrentMap;

import gov.nist.javax.sip.header.ims.PAssertedIdentityHeader;
import io.undertow.security.idm.Account;
import io.undertow.security.idm.IdentityManager;
import io.undertow.security.idm.PasswordCredential;
import io.undertow.servlet.api.AuthMethodConfig;
import io.undertow.servlet.api.Deployment;
import io.undertow.servlet.api.SecurityConstraint;
import io.undertow.servlet.api.ServletInfo;
import io.undertow.servlet.api.WebResourceCollection;

import javax.servlet.sip.SipServletResponse;
import javax.sip.SipStack;
import javax.sip.address.SipURI;
import javax.sip.address.TelURL;

import org.apache.log4j.Logger;
import org.jboss.as.security.plugins.SecurityDomainContext;
import org.jboss.security.AuthenticationManager;
import org.jboss.security.SecurityContext;
import org.jboss.security.SecurityRolesAssociation;
import org.jboss.security.authentication.JBossCachedAuthenticationManager;
import org.jboss.security.authentication.JBossCachedAuthenticationManager.DomainInfo;
import org.mobicents.servlet.sip.core.SipContext;
import org.mobicents.servlet.sip.core.message.MobicentsSipServletRequest;
import org.mobicents.servlet.sip.core.message.MobicentsSipServletResponse;
import org.mobicents.servlet.sip.core.security.MobicentsSipLoginConfig;
import org.mobicents.servlet.sip.core.security.SipPrincipal;
import org.mobicents.servlet.sip.security.SIPSecurityConstants;
import org.mobicents.servlet.sip.security.SecurityActions;
import org.mobicents.servlet.sip.undertow.SipContextImpl;
import org.mobicents.servlet.sip.undertow.SipLoginConfig;
import org.mobicents.servlet.sip.undertow.SipSecurityCollection;
import org.mobicents.servlet.sip.undertow.SipSecurityConstraint;
import org.mobicents.servlet.sip.undertow.security.authentication.SipDigestAuthenticationMechanism;
import org.wildfly.extension.undertow.security.JAASIdentityManagerImpl;

/**
 *
 * This class is based org.mobicents.servlet.sip.catalina.security.SipSecurityUtils class from sip-servlet-as7 project,
 * re-implemented for jboss as10 (wildfly) by:
 *
 * @author [email protected]
 *
 */
public class SipSecurityUtils {
    private static final Logger log = Logger.getLogger(SipSecurityUtils.class);
    private SipContext sipStandardContext;

    public SipSecurityUtils(SipContext sipContext) {
        this.sipStandardContext = sipContext;
    }

    public boolean authenticate(MobicentsSipServletRequest request, SipSecurityConstraint sipConstraint,
            ServletInfo servletInfo, SipStack sipStack) {
        boolean authenticated = false;
        SipLoginConfig loginConfig = (SipLoginConfig) sipStandardContext.getSipLoginConfig();
        try {
            if (loginConfig != null) {
                for (AuthMethodConfig authmethodConfig : loginConfig.getAuthMethods()) {
                    String authMethod = authmethodConfig.getName();
                    if (authMethod != null) {
                        // (1) First check for Proxy Asserted Identity
                        String pAssertedIdentitySetting = loginConfig
                                .getIdentitySchemeSettings(MobicentsSipLoginConfig.IDENTITY_SCHEME_P_ASSERTED);
                        if (pAssertedIdentitySetting != null) {
                            if (request.getHeader(PAssertedIdentityHeader.NAME) != null) {
                                String pAssertedHeaderValue = request.getHeader(PAssertedIdentityHeader.NAME);

                                // If P-Identity is required we must send error message immediately
                                if (pAssertedHeaderValue == null
                                        && MobicentsSipLoginConfig.IDENTITY_SCHEME_REQUIRED.equals(pAssertedIdentitySetting)) {
                                    request.createResponse(428, "P-Asserted-Identity header is required!").send();
                                    return false;
                                }
                                javax.sip.address.Address address = sipStandardContext.getSipApplicationDispatcher()
                                        .getSipFactory().getAddressFactory().createAddress(pAssertedHeaderValue);
                                String username = null;
                                if (address.getURI().isSipURI()) {
                                    SipURI sipUri = (SipURI) address.getURI();
                                    username = sipUri.getUser();
                                } else {
                                    TelURL telUri = (TelURL) address.getURI();
                                    username = telUri.getPhoneNumber();
                                }
                                SipPrincipal principal = impersonatePrincipal(username,
                                        ((SipContextImpl) sipStandardContext).getDeployment(), servletInfo,
                                        ((SipContextImpl) sipStandardContext).getSecurityDomain(),
                                        ((SipLoginConfig) sipStandardContext.getSipLoginConfig()).getRealmName());

                                if (principal != null) {
                                    authenticated = true;
                                    request.setUserPrincipal(principal);
                                    request.getSipSession().setUserPrincipal(principal);
                                    log.debug("P-Asserted-Identity authetication successful for user: " + username);
                                }
                            }
                        }
                        // (2) Then if P-Identity has failed and is not required attempt DIGEST auth
                        if (!authenticated && authMethod.equalsIgnoreCase("DIGEST")) {
                            SipDigestAuthenticationMechanism digestAuthenticator = new SipDigestAuthenticationMechanism(
                                    ((SipLoginConfig) sipStandardContext.getSipLoginConfig()).getRealmName(),
                                    sipStandardContext.getSipApplicationDispatcher().getSipFactory().getHeaderFactory());
                            digestAuthenticator.setContext(sipStandardContext);
                            MobicentsSipServletResponse response = createErrorResponse(request, sipConstraint);
                            authenticated = digestAuthenticator.authenticate(request, response, loginConfig,
                                    ((SipContextImpl) sipStandardContext).getSecurityDomain(),
                                    ((SipContextImpl) sipStandardContext).getDeployment(), servletInfo, sipStack);
                            request.setUserPrincipal(digestAuthenticator.getPrincipal());
                        } else if (authMethod.equalsIgnoreCase("BASIC")) {
                            throw new IllegalStateException("Basic authentication not supported in JSR 289");
                        }
                    }
                }
            } else {
                log.debug("No login configuration found in sip.xml. We won't authenticate.");
                return true; // There is no auth config in sip.xml. So don't authenticate.
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return authenticated;
    }

    private static MobicentsSipServletResponse createErrorResponse(MobicentsSipServletRequest request,
            SipSecurityConstraint sipConstraint) {
        SipServletResponse response = null;
        if (sipConstraint.isProxyAuthentication()) {
            response = (MobicentsSipServletResponse) request
                    .createResponse(MobicentsSipServletResponse.SC_PROXY_AUTHENTICATION_REQUIRED);
        } else {
            response = (MobicentsSipServletResponse) request.createResponse(MobicentsSipServletResponse.SC_UNAUTHORIZED);
        }
        return (MobicentsSipServletResponse) response;
    }

    public boolean authorize(MobicentsSipServletRequest request, ServletInfo servletInfo, SipStack sipStack) {
        boolean allConstrainsSatisfied = true;
        Object[] constraints = ((SipContextImpl) sipStandardContext).getDeploymentInfoFacade().getDeploymentInfo()
                .getSecurityConstraints().toArray();

        // If we have no constraints, just authorize the request;
        if (constraints.length == 0) {
            return true;
        }

        for (Object constraint : constraints) {
            if (constraint instanceof SipSecurityConstraint) {
                SipSecurityConstraint sipConstraint = (SipSecurityConstraint) constraint;
                for (WebResourceCollection security : sipConstraint.getWebResourceCollections()) {

                    // For each secured resource see if it's bound to the current
                    // request method and servlet name.
                    SipSecurityCollection sipSecurity = (SipSecurityCollection) security;
                    String servletName = request.getSipSession().getHandler();
                    if (sipSecurity.findMethod(request.getMethod()) && sipSecurity.findServletName(servletName)) {
                        boolean constraintSatisfied = false;
                        // If yes, see if the current user is in a role compatible with the
                        // required roles for the resource.
                        if (authenticate(request, sipConstraint, servletInfo, sipStack)) {
                            UndertowSipPrincipal principal = (UndertowSipPrincipal) request.getUserPrincipal();
                            if (principal == null)
                                return false;

                            for (String assignedRole : ((SecurityConstraint) constraint).getRolesAllowed()) {
                                if (principal.isUserInRole(assignedRole)) {
                                    constraintSatisfied = true;
                                    break;
                                }
                            }
                        }
                        if (!constraintSatisfied) {
                            allConstrainsSatisfied = false;
                            log.error("Constraint \"" + sipConstraint.getDisplayName() + "\" not satifsied");
                        }
                    }
                }
            }
        }
        return allConstrainsSatisfied;
    }

    /*
     * This method attempts to obtain the Principal of a user from an auth cache without having to authenticate with a password
     * or certificate. If cache-entry is set in standalone.xml, this method tries to use reflection to get the data FIXME:
     * implementing security cache flush
     */
    public static SipPrincipal impersonatePrincipal(String username, Deployment deployment, ServletInfo servletInfo,
            String securityDomain, String realmName) {
        if (username == null) {
            return null;
        }

        final IdentityManager identityManager = deployment.getDeploymentInfo().getIdentityManager();

        PasswordCredential credential = new PasswordCredential("".toCharArray());

        // get securityDomainContext from identityManager
        if (identityManager instanceof JAASIdentityManagerImpl) {
            Field securityDomainContextField = null;
            Field domainCacheField = null;
            Field credentialField = null;

            try {
                securityDomainContextField = JAASIdentityManagerImpl.class.getDeclaredField("securityDomainContext");
                securityDomainContextField.setAccessible(true);

                SecurityDomainContext securityDomainContext = (SecurityDomainContext) securityDomainContextField
                        .get(identityManager);

                if (securityDomainContext != null) {
                    AuthenticationManager authManager = securityDomainContext.getAuthenticationManager();

                    // gets the cache from the manager:
                    if (authManager != null && authManager instanceof JBossCachedAuthenticationManager) {
                        domainCacheField = JBossCachedAuthenticationManager.class.getDeclaredField("domainCache");
                        domainCacheField.setAccessible(true);

                        ConcurrentMap domainCache = (ConcurrentMap) domainCacheField
                                .get(authManager);
                        if (domainCache != null) {
                            for (Principal p : domainCache.keySet()) {
                                if (username.equalsIgnoreCase(p.getName())) {
                                    DomainInfo d = domainCache.get(p);

                                    // gets the credential from the stored DomainInfo:
                                    credentialField = DomainInfo.class.getDeclaredField("credential");
                                    credentialField.setAccessible(true);

                                    credential = new PasswordCredential((char[]) credentialField.get(d));

                                    break;
                                }
                            }
                        }
                    }
                }
            } catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException | SecurityException e) {
                log.warn("Exception occured, while try to impersonate the security principal, please check secuirty settings!",
                        e);
            } finally {
                if (securityDomainContextField != null) {
                    securityDomainContextField.setAccessible(false);
                }
                if (domainCacheField != null) {
                    domainCacheField.setAccessible(false);
                }
                if (credentialField != null) {
                    credentialField.setAccessible(false);
                }
            }
        }

        // taken from
        // https://github.com/jbossas/jboss-as/blob/7.1.2.Final/web/src/main/java/org/jboss/as/web/security/SecurityContextAssociationValve.java#L86
        SecurityContext sc = SecurityActions.getSecurityContext();

        if (sc == null) {
            if (log.isDebugEnabled()) {
                log.debug("Security Domain " + securityDomain + " for Realm " + realmName);
            }
            if (securityDomain == null) {
                if (log.isDebugEnabled()) {
                    log.debug("Security Domain is null using default security domain "
                            + SIPSecurityConstants.DEFAULT_SIP_APPLICATION_POLICY + " for Realm " + realmName);
                }
                securityDomain = SIPSecurityConstants.DEFAULT_SIP_APPLICATION_POLICY;
            }
            sc = SecurityActions.createSecurityContext(securityDomain);

            SecurityActions.setSecurityContextOnAssociation(sc);
        }

        Account account = null;
        try {
            account = identityManager.verify(username, credential);
        } finally {
            SecurityActions.clearSecurityContext();
            SecurityRolesAssociation.setSecurityRoles(null);
        }

        if (account != null) {
            return new UndertowSipPrincipal(account, deployment, servletInfo);
        } else {
            return null;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy