CVE-2017-4952 (Java, published 2018-05-02T14:29Z)
Description: value: VMware Xenon 1.x, prior to 1.5.4-CR7_1, 1.5.7_7, 1.5.4-CR6_2, 1.3.7-CR1_2, 1.1.0-CR0-3, 1.1.0-CR3_1,1.4.2-CR4_1, and 1.5.4_8, contains an authentication bypass vulnerability due to insufficient access controls for utility endpoints. Successful exploitation of this issue may result in information disclosure.
Severity: MEDIUM (CVSS v3 base score: 7.5)
Weakness: CWE-732 \u2014 Incorrect Permission Assignment for Critical Resource
The product specifies permissions for a security-critical resource in a way that allows that resource to be read or modified by unintended actors.
Repository: https://github.com/vmware/xenon
Files changed:
xenon-common/src/main/java/com/vmware/xenon/common/UtilityService.java
xenon-common/src/test/java/com/vmware/xenon/common/TestAuthorization.java
xenon-common/src/test/java/com/vmware/xenon/common/TestExampleServiceHost.java
xenon-common/src/test/java/com/vmware/xenon/common/TestServiceHost.java
xenon-common/src/test/java/com/vmware/xenon/common/TestSubscriptions.java
xenon-common/src/test/java/com/vmware/xenon/common/TestUtilityService.java
xenon-common/src/test/java/com/vmware/xenon/common/test/AuthorizationHelper.java
xenon-common/src/test/java/com/vmware/xenon/common/test/VerificationHost.java
xenon-common/src/test/java/com/vmware/xenon/services/common/TestNodeGroupService.java
Commit message: Add auth to UtilityService

Extend auth check to UtiliyService endpoints: stats, config, subscription

Issue: VRXEN-5

Change-Id: I52a4b4a42731c244a97f97610dcaddb6837e67fb


Vulnerable code:
        op.complete();
        URI usersLink = UriUtils.buildUri(hostUri, UserService.FACTORY_LINK);
        // wait for factory availability
        this.host.waitForReplicatedFactoryServiceAvailable(usersLink);

        this.host.assumeIdentity(userPath);
        return this.host.getServiceStats(managementServiceUri)
        groupHost.setSystemAuthorizationContext();
                    Operation.createGet(UriUtils.buildStatsUri(peer, ExampleService.FACTORY_LINK)));

Fixed code:

        String suffix = UriUtils.buildUriPath(UriUtils.URI_PATH_CHAR, UriUtils.getLastPathSegment(op.getUri()));

        // allow access to ui endpoint
        if (ServiceHost.SERVICE_URI_SUFFIX_UI.equals(suffix)) {
            op.complete();
            return;
        }

        ServiceDocument doc = new ServiceDocument();
        if (this.parent.getOptions().contains(ServiceOption.FACTORY_ITEM)) {
            doc.documentSelfLink = UriUtils.buildUriPath(UriUtils.getParentPath(this.parent.getSelfLink()), suffix);
        } else {
            doc.documentSelfLink = UriUtils.buildUriPath(this.parent.getSelfLink(), suffix);
        }

        doc.documentKind = Utils.buildKind(this.parent.getStateType());
        if (getHost().isAuthorized(this.parent, doc, op)) {
            op.complete();
            return;
        }

        op.fail(Operation.STATUS_CODE_FORBIDDEN);
        OperationContext.setAuthorizationContext(this.host.getSystemAuthorizationContext());
        OperationContext.setAuthorizationContext(null);
        OperationContext.setAuthorizationContext(this.host.getSystemAuthorizationContext());
        OperationContext.setAuthorizationContext(null);
        // do GET on factory /stats, we should get 403
        Operation statsGet = Operation.createGet(this.host,
                ExampleService.FACTORY_LINK + ServiceHost.SERVICE_URI_SUFFIX_STATS);
        this.host.sendAndWaitExpectFailure(statsGet, Operation.STATUS_CODE_FORBIDDEN);

        // do GET on factory /config, we should get 403
        Operation configGet = Operation.createGet(this.host,
                ExampleService.FACTORY_LINK + ServiceHost.SERVICE_URI_SUFFIX_CONFIG);
        this.host.sendAndWaitExpectFailure(configGet, Operation.STATUS_CODE_FORBIDDEN);

        // do GET on utility suffixes in example child services, we should get 403
        for (URI childUri : exampleServices.keySet()) {
            statsGet = Operation.createGet(this.host,
                    childUri.getPath() + ServiceHost.SERVICE_URI_SUFFIX_STATS);
            this.host.sendAndWaitExpectFailure(statsGet, Operation.STATUS_CODE_FORBIDDEN);
            configGet = Operation.createGet(this.host,
                    childUri.getPath() + ServiceHost.SERVICE_URI_SUFFIX_CONFIG);
            this.host.sendAndWaitExpectFailure(configGet, Operation.STATUS_CODE_FORBIDDEN);
        }

        // do GET on utility suffixes in example child services, we should get 200
        for (URI childUri : exampleServices.keySet()) {
            statsGet = Operation.createGet(this.host,
                    childUri.getPath() + ServiceHost.SERVICE_URI_SUFFIX_STATS);
            statsGet.addRequestHeader(Operation.REQUEST_AUTH_TOKEN_HEADER, authToken);
            this.host.sendAndWaitExpectSuccess(statsGet);
        }

        this.host.setSystemAuthorizationContext();
        this.host.resetSystemAuthorizationContext();

        this.host.assumeIdentity(userPath);


        this.host.setSystemAuthorizationContext();
        this.host.resetSystemAuthorizationContext();
        this.host.setSystemAuthorizationContext();
        this.host.resetSystemAuthorizationContext();
        ServiceStat stats = this.host.getServiceStats(managementServiceUri)
        return stats;

            hostWithAuth.setSystemAuthorizationContext();
            hostWithAuth.resetAuthorizationContext();
import static org.junit.Assert.assertNotEquals;
import static com.vmware.xenon.common.ServiceHost.SERVICE_URI_SUFFIX_SYNCHRONIZATION;
import static com.vmware.xenon.common.ServiceHost.SERVICE_URI_SUFFIX_TEMPLATE;
import static com.vmware.xenon.common.ServiceHost.SERVICE_URI_SUFFIX_UI;

import com.vmware.xenon.common.test.AuthTestUtils;
import com.vmware.xenon.common.test.TestRequestSender;
import com.vmware.xenon.common.test.TestRequestSender.FailureResponse;
import com.vmware.xenon.common.test.VerificationHost;
import com.vmware.xenon.services.common.AuthorizationContextService;
import com.vmware.xenon.services.common.QueryTask.Query;
    @Test
    public void endpointAuthorization() throws Throwable {
        VerificationHost host = VerificationHost.create(0);
        host.setAuthorizationService(new AuthorizationContextService());
        host.setAuthorizationEnabled(true);
        host.setMaintenanceIntervalMicros(TimeUnit.MILLISECONDS.toMicros(100));
        host.start();

        TestRequestSender sender = host.getTestRequestSender();

        host.setSystemAuthorizationContext();
        host.waitForReplicatedFactoryServiceAvailable(UriUtils.buildUri(host, ExampleService.FACTORY_LINK));

        String exampleUser = "example@vmware.com";
        String examplePass = "password";
        TestContext authCtx = host.testCreate(1);
        AuthorizationSetupHelper.create()
                .setHost(host)
                .setUserEmail(exampleUser)
                .setUserPassword(examplePass)
                .setResourceQuery(Query.Builder.create()
                        .addFieldClause(ServiceDocument.FIELD_NAME_KIND, Utils.buildKind(ExampleServiceState.class))
                        .build())
                .setCompletion(authCtx.getCompletion())
                .start();
        authCtx.await();

        // create a sample service
        ExampleServiceState doc = new ExampleServiceState();
        doc.name = "foo";
        doc.documentSelfLink = "foo";

        Operation post = Operation.createPost(host, ExampleService.FACTORY_LINK).setBody(doc);
        ExampleServiceState postResult = sender.sendAndWait(post, ExampleServiceState.class);

        host.resetAuthorizationContext();

        URI factoryAvailableUri = UriUtils.buildAvailableUri(host, ExampleService.FACTORY_LINK);
        URI factoryStatsUri = UriUtils.buildStatsUri(host, ExampleService.FACTORY_LINK);
        URI factoryConfigUri = UriUtils.buildConfigUri(host, ExampleService.FACTORY_LINK);
        URI factorySubscriptionUri = UriUtils.buildSubscriptionUri(host, ExampleService.FACTORY_LINK);
        URI factoryTemplateUri = UriUtils.buildUri(host, UriUtils.buildUriPath(ExampleService.FACTORY_LINK, SERVICE_URI_SUFFIX_TEMPLATE));
        URI factorySynchUri = UriUtils.buildUri(host, UriUtils.buildUriPath(ExampleService.FACTORY_LINK, SERVICE_URI_SUFFIX_SYNCHRONIZATION));
        URI factoryUiUri = UriUtils.buildUri(host, UriUtils.buildUriPath(ExampleService.FACTORY_LINK, SERVICE_URI_SUFFIX_UI));

        URI serviceAvailableUri = UriUtils.buildAvailableUri(host, postResult.documentSelfLink);
        URI serviceStatsUri = UriUtils.buildStatsUri(host, postResult.documentSelfLink);
        URI serviceConfigUri = UriUtils.buildConfigUri(host, postResult.documentSelfLink);
        URI serviceSubscriptionUri = UriUtils.buildSubscriptionUri(host, postResult.documentSelfLink);
        URI serviceTemplateUri = UriUtils.buildUri(host, UriUtils.buildUriPath(postResult.documentSelfLink, SERVICE_URI_SUFFIX_TEMPLATE));
        URI serviceSynchUri = UriUtils.buildUri(host, UriUtils.buildUriPath(postResult.documentSelfLink, SERVICE_URI_SUFFIX_SYNCHRONIZATION));
        URI serviceUiUri = UriUtils.buildUri(host, UriUtils.buildUriPath(postResult.documentSelfLink, SERVICE_URI_SUFFIX_UI));

        // check non-authenticated user receives forbidden response
        FailureResponse failureResponse;
        Operation uiOpResult;

        // check factory endpoints
        failureResponse = sender.sendAndWaitFailure(Operation.createGet(factoryAvailableUri));
        assertEquals(Operation.STATUS_CODE_FORBIDDEN, failureResponse.op.getStatusCode());

        failureResponse = sender.sendAndWaitFailure(Operation.createGet(factoryStatsUri));
        assertEquals(Operation.STATUS_CODE_FORBIDDEN, failureResponse.op.getStatusCode());

        failureResponse = sender.sendAndWaitFailure(Operation.createGet(factoryConfigUri));
        assertEquals(Operation.STATUS_CODE_FORBIDDEN, failureResponse.op.getStatusCode());

        failureResponse = sender.sendAndWaitFailure(Operation.createGet(factorySubscriptionUri));
        assertEquals(Operation.STATUS_CODE_FORBIDDEN, failureResponse.op.getStatusCode());

        failureResponse = sender.sendAndWaitFailure(Operation.createGet(factoryTemplateUri));
        assertEquals(Operation.STATUS_CODE_FORBIDDEN, failureResponse.op.getStatusCode());

        failureResponse = sender.sendAndWaitFailure(Operation.createGet(factorySynchUri));
        assertEquals(Operation.STATUS_CODE_FORBIDDEN, failureResponse.op.getStatusCode());

        uiOpResult = sender.sendAndWait(Operation.createGet(factoryUiUri));
        assertNotEquals(Operation.STATUS_CODE_FORBIDDEN, uiOpResult.getStatusCode());

        // check service endpoints
        failureResponse = sender.sendAndWaitFailure(Operation.createGet(serviceAvailableUri));
        assertEquals(Operation.STATUS_CODE_FORBIDDEN, failureResponse.op.getStatusCode());

        failureResponse = sender.sendAndWaitFailure(Operation.createGet(serviceStatsUri));
        assertEquals(Operation.STATUS_CODE_FORBIDDEN, failureResponse.op.getStatusCode());

        failureResponse = sender.sendAndWaitFailure(Operation.createGet(serviceConfigUri));
        assertEquals(Operation.STATUS_CODE_FORBIDDEN, failureResponse.op.getStatusCode());

        failureResponse = sender.sendAndWaitFailure(Operation.createGet(serviceSubscriptionUri));
        assertEquals(Operation.STATUS_CODE_FORBIDDEN, failureResponse.op.getStatusCode());

        failureResponse = sender.sendAndWaitFailure(Operation.createGet(serviceTemplateUri));
        assertEquals(Operation.STATUS_CODE_FORBIDDEN, failureResponse.op.getStatusCode());

        failureResponse = sender.sendAndWaitFailure(Operation.createGet(serviceSynchUri));
        assertEquals(Operation.STATUS_CODE_FORBIDDEN, failureResponse.op.getStatusCode());

        uiOpResult = sender.sendAndWait(Operation.createGet(serviceUiUri));
        assertNotEquals(Operation.STATUS_CODE_FORBIDDEN, uiOpResult.getStatusCode());


        // check authenticated user does NOT receive forbidden response
        AuthTestUtils.login(host, exampleUser, examplePass);

        Operation response;

        // check factory endpoints
        response = sender.sendAndWait(Operation.createGet(factoryAvailableUri));
        assertNotEquals(Operation.STATUS_CODE_FORBIDDEN, response.getStatusCode());

        response = sender.sendAndWait(Operation.createGet(factoryStatsUri));
        assertNotEquals(Operation.STATUS_CODE_FORBIDDEN, response.getStatusCode());

        response = sender.sendAndWait(Operation.createGet(factoryConfigUri));
        assertNotEquals(Operation.STATUS_CODE_FORBIDDEN, response.getStatusCode());

        response = sender.sendAndWait(Operation.createGet(factorySubscriptionUri));
        assertNotEquals(Operation.STATUS_CODE_FORBIDDEN, response.getStatusCode());

        response = sender.sendAndWait(Operation.createGet(factoryTemplateUri));
        assertNotEquals(Operation.STATUS_CODE_FORBIDDEN, response.getStatusCode());

        failureResponse = sender.sendAndWaitFailure(Operation.createGet(factorySynchUri));
        assertNotEquals(Operation.STATUS_CODE_FORBIDDEN, failureResponse.op.getStatusCode());

        response = sender.sendAndWait(Operation.createGet(factoryUiUri));
        assertNotEquals(Operation.STATUS_CODE_FORBIDDEN, response.getStatusCode());


        // check service endpoints
        response = sender.sendAndWait(Operation.createGet(serviceAvailableUri));
        assertNotEquals(Operation.STATUS_CODE_FORBIDDEN, response.getStatusCode());

        response = sender.sendAndWait(Operation.createGet(serviceStatsUri));
        assertNotEquals(Operation.STATUS_CODE_FORBIDDEN, response.getStatusCode());

        response = sender.sendAndWait(Operation.createGet(serviceConfigUri));
        assertNotEquals(Operation.STATUS_CODE_FORBIDDEN, response.getStatusCode());

        response = sender.sendAndWait(Operation.createGet(serviceSubscriptionUri));
        assertNotEquals(Operation.STATUS_CODE_FORBIDDEN, response.getStatusCode());

        response = sender.sendAndWait(Operation.createGet(serviceTemplateUri));
        assertNotEquals(Operation.STATUS_CODE_FORBIDDEN, response.getStatusCode());

        failureResponse = sender.sendAndWaitFailure(Operation.createGet(serviceSynchUri));
        assertNotEquals(Operation.STATUS_CODE_FORBIDDEN, failureResponse.op.getStatusCode());

        response = sender.sendAndWait(Operation.createGet(serviceUiUri));
        assertNotEquals(Operation.STATUS_CODE_FORBIDDEN, response.getStatusCode());

    }

import com.vmware.xenon.services.common.ExampleService;
        // Create resource group to allow access on utility paths
        String statsResourceGroupLink = createResourceGroup(target, "stats-resource-group",
                Builder.create()
                        .addFieldClause(
                                ServiceDocument.FIELD_NAME_SELF_LINK,
                                ExampleService.FACTORY_LINK + ServiceHost.SERVICE_URI_SUFFIX_STATS)
                        .build());

        String subscriptionsResourceGroupLink = createResourceGroup(target, "subs-resource-group",
                Builder.create()
                        .addFieldClause(
                                ServiceDocument.FIELD_NAME_SELF_LINK,
                                ServiceUriPaths.CORE_LOCAL_QUERY_TASKS
                                        + ServiceHost.SERVICE_URI_SUFFIX_SUBSCRIPTIONS)
                        .build());


        // Create role authorizing access to /stats
        paths.add(createRole(target, userGroupLink, statsResourceGroupLink,
                new HashSet<>(
                        Arrays.asList(Action.GET, Action.POST, Action.PATCH, Action.DELETE))));

        // Create role authorizing access to /subscriptions of query tasks
        paths.add(createRole(target, userGroupLink, subscriptionsResourceGroupLink,
                new HashSet<>(
                        Arrays.asList(Action.GET, Action.POST, Action.PATCH, Action.DELETE))));
import com.vmware.xenon.common.OperationContext;
        AuthorizationContext ctx = null;
        if (this.isAuthorizationEnabled()) {
            ctx = OperationContext.getAuthorizationContext();
            this.setSystemAuthorizationContext();
        }
        if (this.isAuthorizationEnabled()) {
            this.setAuthorizationContext(ctx);
        }
        groupHost.setSystemAuthorizationContext();

                    Operation.createGet(UriUtils.buildUri(peer, ExampleService.FACTORY_LINK)));