mirror of
https://github.com/open62541/open62541.git
synced 2026-10-17 04:42:09 +08:00
fix(server): offer user tokens on #None only if they can be secured
Without encryption support, the #None endpoint offered x509 and password tokens that ActivateSession always rejects. The endpoint check now runs in every build. It removes x509 tokens without the password warning.
This commit is contained in:
committed by
Julius Pfrommer
parent
8572ffcd16
commit
bf8d24e0cf
@@ -404,11 +404,11 @@ updateEndpointUserIdentityToken(UA_Server *server,
|
||||
utp = &ed->userIdentityTokens[ed->userIdentityTokensSize - 1];
|
||||
UA_String_clear(&utp->securityPolicyUri);
|
||||
|
||||
#ifdef UA_ENABLE_ENCRYPTION
|
||||
/* Anonymous tokens don't need encryption. All other tokens require
|
||||
* encryption with the exception of Username/Password if also the
|
||||
* allowNonePolicyPassword option has been set. The same logic is used
|
||||
* in selectEndpointAndTokenPolicy (ua_services_session.c). */
|
||||
* in selectEndpointAndTokenPolicy (ua_services_session.c), which
|
||||
* rejects the tokens removed here. */
|
||||
if(utp->tokenType != UA_USERTOKENTYPE_ANONYMOUS &&
|
||||
UA_String_equal(&ed->securityPolicyUri, &UA_SECURITY_POLICY_NONE_URI) &&
|
||||
(!sc->allowNonePolicyPassword || utp->tokenType != UA_USERTOKENTYPE_USERNAME)) {
|
||||
@@ -420,11 +420,14 @@ updateEndpointUserIdentityToken(UA_Server *server,
|
||||
else
|
||||
encSP = getSecurityPolicyByUri(server, &ed->securityPolicyUri);
|
||||
if(!encSP) {
|
||||
/* No encrypted SecurityPolicy available */
|
||||
UA_LOG_WARNING(sc->logging, UA_LOGCATEGORY_CLIENT,
|
||||
"Removing a UserTokenPolicy that would allow the "
|
||||
"password to be transmitted without encryption "
|
||||
"(Can be enabled via config->allowNonePolicyPassword)");
|
||||
/* No encrypted SecurityPolicy available. An x509 token
|
||||
* carries no secret and is offered by default with a session
|
||||
* PKI. So it is removed without a warning. */
|
||||
if(utp->tokenType != UA_USERTOKENTYPE_CERTIFICATE)
|
||||
UA_LOG_WARNING(sc->logging, UA_LOGCATEGORY_CLIENT,
|
||||
"Removing a UserTokenPolicy that would allow the "
|
||||
"password to be transmitted without encryption "
|
||||
"(Can be enabled via config->allowNonePolicyPassword)");
|
||||
UA_StatusCode res2 =
|
||||
UA_Array_resize((void **)&ed->userIdentityTokens,
|
||||
&ed->userIdentityTokensSize,
|
||||
@@ -435,7 +438,6 @@ updateEndpointUserIdentityToken(UA_Server *server,
|
||||
}
|
||||
res |= UA_String_copy(&encSP->policyUri, &utp->securityPolicyUri);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Append the SecurityMode and SecurityPolicy postfix to the PolicyId to
|
||||
* make it unique */
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <open62541/server_config_default.h>
|
||||
#include <open62541/types.h>
|
||||
#include <open62541/transport_generated.h>
|
||||
#include <open62541/plugin/accesscontrol_default.h>
|
||||
|
||||
#include "server/ua_server_internal.h"
|
||||
#include "server/ua_services.h"
|
||||
@@ -139,6 +140,61 @@ START_TEST(checkGetLifecycleState) {
|
||||
ck_assert_int_eq(state, UA_LIFECYCLESTATE_STOPPED);
|
||||
} END_TEST
|
||||
|
||||
static size_t
|
||||
countNoneEndpointTokens(UA_UserTokenType tokenType) {
|
||||
UA_GetEndpointsRequest request;
|
||||
UA_GetEndpointsRequest_init(&request);
|
||||
request.endpointUrl = UA_STRING("opc.tcp://localhost:4840");
|
||||
UA_GetEndpointsResponse response;
|
||||
UA_GetEndpointsResponse_init(&response);
|
||||
lockServer(server);
|
||||
Service_GetEndpoints(server, &server->adminSession, &request, &response);
|
||||
unlockServer(server);
|
||||
ck_assert_uint_eq(response.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
|
||||
|
||||
size_t count = 0;
|
||||
for(size_t i = 0; i < response.endpointsSize; i++) {
|
||||
const UA_EndpointDescription *ep = &response.endpoints[i];
|
||||
if(!UA_String_equal(&ep->securityPolicyUri, &UA_SECURITY_POLICY_NONE_URI))
|
||||
continue;
|
||||
for(size_t j = 0; j < ep->userIdentityTokensSize; j++) {
|
||||
if(ep->userIdentityTokens[j].tokenType == tokenType)
|
||||
count++;
|
||||
}
|
||||
}
|
||||
UA_GetEndpointsResponse_clear(&response);
|
||||
return count;
|
||||
}
|
||||
|
||||
/* Without an encrypting SecurityPolicy, ActivateSession rejects x509 and
|
||||
* password tokens on #None. So the #None endpoint does not offer them. */
|
||||
START_TEST(checkNoneEndpointUserTokens) {
|
||||
UA_ServerConfig *config = UA_Server_getConfig(server);
|
||||
UA_UsernamePasswordLogin login = {UA_STRING_STATIC("user"),
|
||||
UA_STRING_STATIC("password")};
|
||||
ck_assert_uint_eq(UA_AccessControl_default(config, true, NULL, 1, &login),
|
||||
UA_STATUSCODE_GOOD);
|
||||
|
||||
/* The AccessControl offers x509, as the session PKI accepts all */
|
||||
UA_Boolean haveX509 = false;
|
||||
for(size_t i = 0; i < config->accessControl.userTokenPoliciesSize; i++) {
|
||||
if(config->accessControl.userTokenPolicies[i].tokenType ==
|
||||
UA_USERTOKENTYPE_CERTIFICATE)
|
||||
haveX509 = true;
|
||||
}
|
||||
ck_assert(haveX509);
|
||||
|
||||
config->allowNonePolicyPassword = false;
|
||||
ck_assert_uint_gt(countNoneEndpointTokens(UA_USERTOKENTYPE_ANONYMOUS), 0);
|
||||
ck_assert_uint_eq(countNoneEndpointTokens(UA_USERTOKENTYPE_USERNAME), 0);
|
||||
ck_assert_uint_eq(countNoneEndpointTokens(UA_USERTOKENTYPE_CERTIFICATE), 0);
|
||||
|
||||
/* Passwords can be allowed without encryption, x509 tokens cannot */
|
||||
config->allowNonePolicyPassword = true;
|
||||
ck_assert_uint_gt(countNoneEndpointTokens(UA_USERTOKENTYPE_USERNAME), 0);
|
||||
ck_assert_uint_eq(countNoneEndpointTokens(UA_USERTOKENTYPE_CERTIFICATE), 0);
|
||||
} END_TEST
|
||||
|
||||
/* ---- Additional coverage tests ---- */
|
||||
|
||||
START_TEST(checkGetDataTypes) {
|
||||
@@ -767,6 +823,7 @@ int main(void) {
|
||||
tcase_add_test(tc_call, checkServer_run);
|
||||
tcase_add_test(tc_call, helloEndpointUrlLimit);
|
||||
tcase_add_test(tc_call, helloTrailingData);
|
||||
tcase_add_test(tc_call, checkNoneEndpointUserTokens);
|
||||
suite_add_tcase(s, tc_call);
|
||||
|
||||
TCase *tc_ext = tcase_create("server - extended");
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include <open62541/plugin/accesscontrol_default.h>
|
||||
#include <open62541/plugin/securitypolicy_default.h>
|
||||
|
||||
#include "server/ua_server_internal.h"
|
||||
#include "server/ua_services.h"
|
||||
#include "thread_wrapper.h"
|
||||
#include "test_helpers.h"
|
||||
#include "../encryption/certificates.h"
|
||||
@@ -205,6 +207,63 @@ START_TEST(testServerCertificateInGetEndpoints) {
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/* Each x509 token must have a SecurityPolicy other than #None, which its
|
||||
* signature needs. */
|
||||
static size_t
|
||||
countCertificateTokens(UA_Server *s, const UA_String *securityPolicyUri) {
|
||||
UA_GetEndpointsRequest request;
|
||||
UA_GetEndpointsRequest_init(&request);
|
||||
request.endpointUrl = UA_STRING("opc.tcp://localhost:4840");
|
||||
UA_GetEndpointsResponse response;
|
||||
UA_GetEndpointsResponse_init(&response);
|
||||
lockServer(s);
|
||||
Service_GetEndpoints(s, &s->adminSession, &request, &response);
|
||||
unlockServer(s);
|
||||
ck_assert_uint_eq(response.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
|
||||
|
||||
const UA_String noneUri = UA_STRING_STATIC(NONE_URI);
|
||||
size_t count = 0;
|
||||
for(size_t i = 0; i < response.endpointsSize; i++) {
|
||||
UA_EndpointDescription *ep = &response.endpoints[i];
|
||||
if(!UA_String_equal(&ep->securityPolicyUri, securityPolicyUri))
|
||||
continue;
|
||||
for(size_t j = 0; j < ep->userIdentityTokensSize; j++) {
|
||||
UA_UserTokenPolicy *utp = &ep->userIdentityTokens[j];
|
||||
if(utp->tokenType != UA_USERTOKENTYPE_CERTIFICATE)
|
||||
continue;
|
||||
const UA_String *tokenUri = (utp->securityPolicyUri.length > 0) ?
|
||||
&utp->securityPolicyUri : &ep->securityPolicyUri;
|
||||
ck_assert(!UA_String_equal(tokenUri, &noneUri));
|
||||
count++;
|
||||
}
|
||||
}
|
||||
UA_GetEndpointsResponse_clear(&response);
|
||||
return count;
|
||||
}
|
||||
|
||||
/* The endpoints choose the SecurityPolicy of the user tokens. So x509 is
|
||||
* offered for a SecurityPolicy added after the AccessControl was set up. */
|
||||
START_TEST(certificateTokenWithLaterSecurityPolicy) {
|
||||
UA_Server *s = UA_Server_newForUnitTest();
|
||||
ck_assert_ptr_ne(s, NULL);
|
||||
UA_ServerConfig *config = UA_Server_getConfig(s);
|
||||
UA_ByteString certificate = {CERT_DER_LENGTH, CERT_DER_DATA};
|
||||
UA_ByteString privateKey = {KEY_DER_LENGTH, KEY_DER_DATA};
|
||||
UA_StatusCode retval =
|
||||
UA_ServerConfig_addSecurityPolicyBasic256Sha256(config, &certificate,
|
||||
&privateKey);
|
||||
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
|
||||
UA_String secureUri = UA_STRING(BASIC256SHA256_URI);
|
||||
retval = UA_ServerConfig_addEndpoint(config, secureUri,
|
||||
UA_MESSAGESECURITYMODE_SIGNANDENCRYPT);
|
||||
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
|
||||
|
||||
UA_String noneUri = UA_STRING(NONE_URI);
|
||||
ck_assert_uint_gt(countCertificateTokens(s, &secureUri), 0);
|
||||
ck_assert_uint_gt(countCertificateTokens(s, &noneUri), 0);
|
||||
UA_Server_delete(s);
|
||||
} END_TEST
|
||||
|
||||
int main(void) {
|
||||
Suite *s = suite_create("Server GetEndpoints");
|
||||
TCase *tc = tcase_create("ServerCertificate present in Endpoint");
|
||||
@@ -212,6 +271,10 @@ int main(void) {
|
||||
tcase_add_test(tc, testServerCertificateInGetEndpoints);
|
||||
suite_add_tcase(s, tc);
|
||||
|
||||
TCase *tc_tokens = tcase_create("Certificate token policies");
|
||||
tcase_add_test(tc_tokens, certificateTokenWithLaterSecurityPolicy);
|
||||
suite_add_tcase(s, tc_tokens);
|
||||
|
||||
SRunner *sr = srunner_create(s);
|
||||
srunner_set_fork_status(sr, CK_NOFORK);
|
||||
srunner_run_all(sr, CK_NORMAL);
|
||||
|
||||
Reference in New Issue
Block a user