Browse Source

feature: access control plugin; give session handle to method callbacks

Julius Pfrommer 8 years ago
parent
commit
860b800b82

+ 2 - 0
CMakeLists.txt

@@ -176,6 +176,7 @@ set(exported_headers ${PROJECT_BINARY_DIR}/src_generated/ua_config.h
                      ${PROJECT_SOURCE_DIR}/include/ua_client.h
                      ${PROJECT_SOURCE_DIR}/include/ua_client_highlevel.h
                      ${PROJECT_SOURCE_DIR}/plugins/ua_network_tcp.h
+                     ${PROJECT_SOURCE_DIR}/plugins/ua_accesscontrol_default.h
                      ${PROJECT_SOURCE_DIR}/plugins/ua_log_stdout.h
                      ${PROJECT_SOURCE_DIR}/plugins/ua_config_standard.h)
 set(internal_headers ${PROJECT_SOURCE_DIR}/deps/queue.h
@@ -233,6 +234,7 @@ set(lib_sources ${PROJECT_SOURCE_DIR}/src/ua_types.c
                 ${PROJECT_SOURCE_DIR}/src/client/ua_client_highlevel_subscriptions.c
                 # plugins and dependencies
                 ${PROJECT_SOURCE_DIR}/plugins/ua_network_tcp.c
+                ${PROJECT_SOURCE_DIR}/plugins/ua_accesscontrol_default.c
                 ${PROJECT_SOURCE_DIR}/plugins/ua_clock.c
                 ${PROJECT_SOURCE_DIR}/plugins/ua_log_stdout.c
                 ${PROJECT_SOURCE_DIR}/plugins/ua_config_standard.c

+ 10 - 7
examples/server.c

@@ -77,7 +77,8 @@ readTimeData(void *handle, const UA_NodeId nodeId, UA_Boolean sourceTimeStamp,
 /* Method Node Example */
 #ifdef UA_ENABLE_METHODCALLS
 static UA_StatusCode
-helloWorld(void *methodHandle, const UA_NodeId objectId,
+helloWorld(void *methodHandle, const UA_NodeId *objectId,
+           const UA_NodeId *sessionId, void *sessionHandle,
            size_t inputSize, const UA_Variant *input,
            size_t outputSize, UA_Variant *output) {
     /* input is a scalar string (checked by the server) */
@@ -94,16 +95,18 @@ helloWorld(void *methodHandle, const UA_NodeId objectId,
 }
 
 static UA_StatusCode
-noargMethod (void *methodHandle, const UA_NodeId objectId,
-           size_t inputSize, const UA_Variant *input,
-           size_t outputSize, UA_Variant *output) {
+noargMethod(void *methodHandle, const UA_NodeId *objectId,
+            const UA_NodeId *sessionId, void *sessionHandle,
+            size_t inputSize, const UA_Variant *input,
+            size_t outputSize, UA_Variant *output) {
     return UA_STATUSCODE_GOOD;
 }
 
 static UA_StatusCode
-outargMethod (void *methodHandle, const UA_NodeId objectId,
-           size_t inputSize, const UA_Variant *input,
-           size_t outputSize, UA_Variant *output) {
+outargMethod(void *methodHandle, const UA_NodeId *objectId,
+            const UA_NodeId *sessionId, void *sessionHandle,
+             size_t inputSize, const UA_Variant *input,
+             size_t outputSize, UA_Variant *output) {
     UA_Int32 out = 42;
     UA_Variant_setScalarCopy(output, &out, &UA_TYPES[UA_TYPES_INT32]);
     return UA_STATUSCODE_GOOD;

+ 6 - 3
examples/server_method.c

@@ -20,7 +20,8 @@ UA_Logger logger = UA_Log_Stdout;
 
 /* Example 1 */
 static UA_StatusCode
-helloWorldMethod(void *handle, const UA_NodeId objectId,
+helloWorldMethod(void *handle, const UA_NodeId *objectId,
+                 const UA_NodeId *sessionId, void *sessionHandle,
                  size_t inputSize, const UA_Variant *input,
                  size_t outputSize, UA_Variant *output) {
     UA_String *inputStr = (UA_String*)input->data;
@@ -38,7 +39,8 @@ helloWorldMethod(void *handle, const UA_NodeId objectId,
 
 /* Example 2 */
 static UA_StatusCode
-IncInt32ArrayValuesMethod(void *handle, const UA_NodeId objectId,
+IncInt32ArrayValuesMethod(void *handle, const UA_NodeId *objectId,
+                          const UA_NodeId *sessionId, void *sessionHandle,
                           size_t inputSize, const UA_Variant *input,
                           size_t outputSize, UA_Variant *output) {
     UA_Variant_setArrayCopy(output, input->data, 5, &UA_TYPES[UA_TYPES_INT32]);
@@ -50,7 +52,8 @@ IncInt32ArrayValuesMethod(void *handle, const UA_NodeId objectId,
 
 /* Example 3 */
 static UA_StatusCode
-fooBarMethod(void *handle, const UA_NodeId objectId,
+fooBarMethod(void *handle, const UA_NodeId *objectId,
+             const UA_NodeId *sessionId, void *sessionHandle,
              size_t inputSize, const UA_Variant *input,
              size_t outputSize, UA_Variant *output) {
     /* the same as helloWorld, but returns foobar */

+ 43 - 6
include/ua_server.h

@@ -80,6 +80,45 @@ struct UA_ServerNetworkLayer {
     void (*deleteMembers)(UA_ServerNetworkLayer *nl);
 };
 
+/**
+ * Access Control
+ * --------------
+ * The access control callback is used to authenticate sessions and grant access
+ * rights accordingly. */
+typedef struct {
+    UA_Boolean enableAnonymousLogin;
+    UA_Boolean enableUsernamePasswordLogin;
+    
+    /* Authenticate a session */
+    UA_StatusCode (*activateSession)(const UA_NodeId *sessionId,
+                                     const UA_ExtensionObject *userIdentityToken,
+                                     void **sessionHandle);
+
+    /* Deauthenticate a session and cleanup */
+    void (*closeSession)(const UA_NodeId *sessionId, void *sessionHandle);
+
+    /* Access control for all nodes*/
+    UA_UInt32 (*getUserRightsMask)(const UA_NodeId *sessionId, void *sessionHandle, const UA_NodeId *nodeId);
+
+    /* Additional access control for variable nodes */
+    UA_Byte (*getUserAccessLevel)(const UA_NodeId *sessionId, void *sessionHandle, const UA_NodeId *nodeId);
+
+    /* Additional access control for method nodes */
+    UA_Boolean (*getUserExecutable)(const UA_NodeId *sessionId, void *sessionHandle, const UA_NodeId *nodeId);
+
+    /* Allow adding a node */
+    UA_Boolean (*allowAddNode)(const UA_NodeId *sessionId, void *sessionHandle, const UA_AddNodesItem *item);
+
+    /* Allow adding a reference */
+    UA_Boolean (*allowAddReference)(const UA_NodeId *sessionId, void *sessionHandle, const UA_AddReferencesItem *item);
+
+    /* Allow deleting a node */
+    UA_Boolean (*allowDeleteNode)(const UA_NodeId *sessionId, void *sessionHandle, const UA_DeleteNodesItem *item);
+
+    /* Allow deleting a reference */
+    UA_Boolean (*allowDeleteReference)(const UA_NodeId *sessionId, void *sessionHandle, const UA_DeleteReferencesItem *item);
+} UA_AccessControl;
+
 /**
  * Server Configuration
  * --------------------
@@ -112,11 +151,8 @@ typedef struct {
     size_t networkLayersSize;
     UA_ServerNetworkLayer *networkLayers;
 
-    /* Login */
-    UA_Boolean enableAnonymousLogin;
-    UA_Boolean enableUsernamePasswordLogin;
-    size_t usernamePasswordLoginsSize;
-    UA_UsernamePasswordLogin* usernamePasswordLogins;
+    /* Access Control */
+    UA_AccessControl accessControl;
 
     /* Limits for SecureChannels */
     UA_UInt16 maxSecureChannels;
@@ -686,7 +722,8 @@ UA_Server_setObjectTypeNode_lifecycleManagement(UA_Server *server,
  * Method Callbacks
  * ~~~~~~~~~~~~~~~~ */
 typedef UA_StatusCode
-(*UA_MethodCallback)(void *methodHandle, const UA_NodeId objectId,
+(*UA_MethodCallback)(void *methodHandle, const UA_NodeId *objectId,
+                     const UA_NodeId *sessionId, void *sessionHandle,
                      size_t inputSize, const UA_Variant *input,
                      size_t outputSize, UA_Variant *output);
 

+ 108 - 0
plugins/ua_accesscontrol_default.c

@@ -0,0 +1,108 @@
+/* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
+ * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
+
+#include "ua_accesscontrol_default.h"
+
+/* We allow login anonymous and with the following username / password. The
+ * access rights are maximally permissive in this example plugin. */
+
+#define ANONYMOUS_POLICY "open62541-anonymous-policy"
+#define USERNAME_POLICY "open62541-username-policy"
+
+#define UA_STRING_STATIC(s) {sizeof(s)-1, (UA_Byte*)s}
+const UA_String ap = UA_STRING_STATIC(ANONYMOUS_POLICY);
+const UA_String up = UA_STRING_STATIC(USERNAME_POLICY);
+
+UA_StatusCode
+activateSession_default(const UA_NodeId *sessionId, const UA_ExtensionObject *userIdentityToken,
+                        void **sessionHandle) {
+    /* Could the token be decoded? */
+    if(userIdentityToken->encoding < UA_EXTENSIONOBJECT_DECODED)
+        return UA_STATUSCODE_BADIDENTITYTOKENINVALID;
+
+    /* anonymous login */
+    if(enableAnonymousLogin &&
+       userIdentityToken->content.decoded.type == &UA_TYPES[UA_TYPES_ANONYMOUSIDENTITYTOKEN]) {
+        const UA_AnonymousIdentityToken *token = userIdentityToken->content.decoded.data;
+
+        /* Compatibility notice: Siemens OPC Scout v10 provides an empty
+         * policyId. This is not compliant. For compatibility we will assume
+         * that empty policyId == ANONYMOUS_POLICY */
+        if(token->policyId.data && !UA_String_equal(&token->policyId, &ap))
+            return UA_STATUSCODE_BADIDENTITYTOKENINVALID;
+
+        *sessionHandle = NULL;
+        return UA_STATUSCODE_GOOD;
+    }
+
+    /* username and password */
+    if(enableUsernamePasswordLogin &&
+       userIdentityToken->content.decoded.type == &UA_TYPES[UA_TYPES_USERNAMEIDENTITYTOKEN]) {
+        const UA_UserNameIdentityToken *token = userIdentityToken->content.decoded.data;
+        if(!UA_String_equal(&token->policyId, &up))
+            return UA_STATUSCODE_BADIDENTITYTOKENINVALID;
+
+        /* empty username and password */
+        if(token->userName.length == 0 && token->password.length == 0)
+            return UA_STATUSCODE_BADIDENTITYTOKENINVALID;
+
+        /* trying to match pw/username */
+        UA_Boolean match = false;
+        for(size_t i = 0; i < usernamePasswordsSize; i++) {
+            const UA_String *user = &usernamePasswords[i].username;
+            const UA_String *pw = &usernamePasswords[i].password;
+            if(UA_String_equal(&token->userName, user) && UA_String_equal(&token->password, pw)) {
+                match = true;
+                break;
+            }
+        }
+        if(!match)
+            return UA_STATUSCODE_BADUSERACCESSDENIED;
+
+        *sessionHandle = NULL;
+        return UA_STATUSCODE_GOOD;
+    }
+
+    /* Unsupported token type */
+    return UA_STATUSCODE_BADIDENTITYTOKENINVALID;
+}
+
+void
+closeSession_default(const UA_NodeId *sessionId, void *sessionHandle) {
+    /* no handle to clean up */
+}
+
+UA_UInt32
+getUserRightsMask_default(const UA_NodeId *sessionId, void *sessionHandle, const UA_NodeId *nodeId) {
+    return 0xFFFFFFFF;
+}
+
+UA_Byte
+getUserAccessLevel_default(const UA_NodeId *sessionId, void *sessionHandle, const UA_NodeId *nodeId) {
+    return 0xFF;
+}
+
+UA_Boolean
+getUserExecutable_default(const UA_NodeId *sessionId, void *sessionHandle, const UA_NodeId *nodeId) {
+    return true;
+}
+
+UA_Boolean
+allowAddNode_default(const UA_NodeId *sessionId, void *sessionHandle, const UA_AddNodesItem *item) {
+    return true;
+}
+
+UA_Boolean
+allowAddReference_default(const UA_NodeId *sessionId, void *sessionHandle, const UA_AddReferencesItem *item) {
+    return true;
+}
+
+UA_Boolean
+allowDeleteNode_default(const UA_NodeId *sessionId, void *sessionHandle, const UA_DeleteNodesItem *item) {
+    return true;
+}
+      
+UA_Boolean
+allowDeleteReference_default(const UA_NodeId *sessionId, void *sessionHandle, const UA_DeleteReferencesItem *item) {
+    return true;
+}

+ 49 - 0
plugins/ua_accesscontrol_default.h

@@ -0,0 +1,49 @@
+/* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
+ * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
+
+#ifndef UA_ACCESSCONTROL_DEFAULT_H_
+#define UA_ACCESSCONTROL_DEFAULT_H_
+
+#include "ua_server.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern const UA_Boolean enableAnonymousLogin;
+extern const UA_Boolean enableUsernamePasswordLogin;
+extern const size_t usernamePasswordsSize;
+extern const UA_UsernamePasswordLogin *usernamePasswords;
+
+UA_EXPORT UA_StatusCode
+activateSession_default(const UA_NodeId *sessionId, const UA_ExtensionObject *userIdentityToken, void **sessionHandle);
+
+UA_EXPORT void
+closeSession_default(const UA_NodeId *sessionId, void *sessionHandle);
+
+UA_EXPORT UA_UInt32
+getUserRightsMask_default(const UA_NodeId *sessionId, void *sessionHandle, const UA_NodeId *nodeId);
+
+UA_EXPORT UA_Byte
+getUserAccessLevel_default(const UA_NodeId *sessionId, void *sessionHandle, const UA_NodeId *nodeId);
+
+UA_EXPORT UA_Boolean
+getUserExecutable_default(const UA_NodeId *sessionId, void *sessionHandle, const UA_NodeId *nodeId);
+
+UA_EXPORT UA_Boolean
+allowAddNode_default(const UA_NodeId *sessionId, void *sessionHandle, const UA_AddNodesItem *item);
+
+UA_EXPORT UA_Boolean
+allowAddReference_default(const UA_NodeId *sessionId, void *sessionHandle, const UA_AddReferencesItem *item);
+
+UA_EXPORT UA_Boolean
+allowDeleteNode_default(const UA_NodeId *sessionId, void *sessionHandle, const UA_DeleteNodesItem *item);
+      
+UA_EXPORT UA_Boolean
+allowDeleteReference_default(const UA_NodeId *sessionId, void *sessionHandle, const UA_DeleteReferencesItem *item);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UA_ACCESSCONTROL_DEFAULT_H_ */

+ 22 - 6
plugins/ua_config_standard.c

@@ -4,6 +4,7 @@
 #include "ua_config_standard.h"
 #include "ua_log_stdout.h"
 #include "ua_network_tcp.h"
+#include "ua_accesscontrol_default.h"
 
 /*******************************/
 /* Default Connection Settings */
@@ -30,7 +31,13 @@ const UA_EXPORT UA_ConnectionConfig UA_ConnectionConfig_standard = {
 #define UA_STRING_STATIC(s) {sizeof(s)-1, (UA_Byte*)s}
 #define UA_STRING_STATIC_NULL {0, NULL}
 
-UA_UsernamePasswordLogin usernamePasswords[2] = {
+/* Access Control */
+#define ENABLEANONYMOUSLOGIN true
+#define ENABLEUSERNAMEPASSWORDLOGIN true
+const UA_Boolean enableAnonymousLogin = ENABLEANONYMOUSLOGIN;
+const UA_Boolean enableUsernamePasswordLogin = ENABLEUSERNAMEPASSWORDLOGIN;
+const size_t usernamePasswordsSize = 2;
+const UA_UsernamePasswordLogin *usernamePasswords = (UA_UsernamePasswordLogin[2]){
     { UA_STRING_STATIC("user1"), UA_STRING_STATIC("password") },
     { UA_STRING_STATIC("user2"), UA_STRING_STATIC("password1") } };
 
@@ -62,11 +69,20 @@ const UA_EXPORT UA_ServerConfig UA_ServerConfig_standard = {
     .networkLayersSize = 0,
     .networkLayers = NULL,
 
-    /* Login */
-    .enableAnonymousLogin = true,
-    .enableUsernamePasswordLogin = true,
-    .usernamePasswordLogins = usernamePasswords,
-    .usernamePasswordLoginsSize = 2,
+    /* Access Control */
+    .accessControl = (UA_AccessControl) {
+        .enableAnonymousLogin = ENABLEANONYMOUSLOGIN,
+        .enableUsernamePasswordLogin = ENABLEUSERNAMEPASSWORDLOGIN,
+        .activateSession = activateSession_default,
+        .closeSession = closeSession_default,
+        .getUserRightsMask = getUserRightsMask_default,
+        .getUserAccessLevel = getUserAccessLevel_default,
+        .getUserExecutable = getUserExecutable_default,
+        .allowAddNode = allowAddNode_default,
+        .allowAddReference = allowAddReference_default,
+        .allowDeleteNode = allowDeleteNode_default,
+        .allowDeleteReference = allowDeleteReference_default
+    },
 
     /* Limits for SecureChannels */
     .maxSecureChannels = 40,

+ 8 - 6
src/server/ua_server.c

@@ -419,8 +419,10 @@ createVariableTypeNode(UA_Server *server, char* name, UA_UInt32 variabletypeid,
 
 #if defined(UA_ENABLE_METHODCALLS) && defined(UA_ENABLE_SUBSCRIPTIONS)
 static UA_StatusCode
-GetMonitoredItems(void *handle, const UA_NodeId objectId, size_t inputSize,
-                  const UA_Variant *input, size_t outputSize, UA_Variant *output) {
+GetMonitoredItems(void *handle, const UA_NodeId *objectId,
+                  const UA_NodeId *sessionId, void *sessionHandle,
+                  size_t inputSize, const UA_Variant *input,
+                  size_t outputSize, UA_Variant *output) {
     UA_UInt32 subscriptionId = *((UA_UInt32*)(input[0].data));
     UA_Session* session = methodCallSession;
     UA_Subscription* subscription = UA_Session_getSubscriptionByID(session, subscriptionId);
@@ -488,21 +490,21 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
             UA_STRING_ALLOC("http://opcfoundation.org/UA-Profile/Transport/uatcp-uasc-uabinary");
 
         size_t policies = 0;
-        if(server->config.enableAnonymousLogin)
+        if(server->config.accessControl.enableAnonymousLogin)
             policies++;
-        if(server->config.enableUsernamePasswordLogin)
+        if(server->config.accessControl.enableUsernamePasswordLogin)
             policies++;
         endpoint->userIdentityTokensSize = policies;
         endpoint->userIdentityTokens = UA_Array_new(policies, &UA_TYPES[UA_TYPES_USERTOKENPOLICY]);
 
         size_t currentIndex = 0;
-        if(server->config.enableAnonymousLogin) {
+        if(server->config.accessControl.enableAnonymousLogin) {
             UA_UserTokenPolicy_init(&endpoint->userIdentityTokens[currentIndex]);
             endpoint->userIdentityTokens[currentIndex].tokenType = UA_USERTOKENTYPE_ANONYMOUS;
             endpoint->userIdentityTokens[currentIndex].policyId = UA_STRING_ALLOC(ANONYMOUS_POLICY);
             currentIndex++;
         }
-        if(server->config.enableUsernamePasswordLogin) {
+        if(server->config.accessControl.enableUsernamePasswordLogin) {
             UA_UserTokenPolicy_init(&endpoint->userIdentityTokens[currentIndex]);
             endpoint->userIdentityTokens[currentIndex].tokenType = UA_USERTOKENTYPE_USERNAME;
             endpoint->userIdentityTokens[currentIndex].policyId = UA_STRING_ALLOC(USERNAME_POLICY);

+ 2 - 1
src/server/ua_services_call.c

@@ -136,7 +136,8 @@ Service_Call_single(UA_Server *server, UA_Session *session,
 #if defined(UA_ENABLE_METHODCALLS) && defined(UA_ENABLE_SUBSCRIPTIONS)
     methodCallSession = session;
 #endif
-    result->statusCode = methodCalled->attachedMethod(methodCalled->methodHandle, withObject->nodeId,
+    result->statusCode = methodCalled->attachedMethod(methodCalled->methodHandle, &withObject->nodeId,
+                                                      &session->sessionId, session->sessionHandle,
                                                       request->inputArgumentsSize, request->inputArguments,
                                                       result->outputArgumentsSize, result->outputArguments);
 #if defined(UA_ENABLE_METHODCALLS) && defined(UA_ENABLE_SUBSCRIPTIONS)

+ 15 - 67
src/server/ua_services_session.c

@@ -31,8 +31,13 @@ void Service_CreateSession(UA_Server *server, UA_SecureChannel *channel,
         return;
     }
 
+    /* Fill the session with more information */
     newSession->maxResponseMessageSize = request->maxResponseMessageSize;
     newSession->maxRequestMessageSize = channel->connection->localConf.maxMessageSize;
+    response->responseHeader.serviceResult |=
+        UA_ApplicationDescription_copy(&request->clientDescription, &newSession->clientDescription);
+
+    /* Prepare the response */
     response->sessionId = newSession->sessionId;
     response->revisedSessionTimeout = (UA_Double)newSession->timeout;
     response->authenticationToken = newSession->authenticationToken;
@@ -41,10 +46,13 @@ void Service_CreateSession(UA_Server *server, UA_SecureChannel *channel,
         response->responseHeader.serviceResult |=
             UA_ByteString_copy(&server->endpointDescriptions->serverCertificate,
                                &response->serverCertificate);
+
+    /* Failure -> remove the session */
     if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) {
         UA_SessionManager_removeSession(&server->sessionManager, &newSession->authenticationToken);
          return;
     }
+
     UA_LOG_DEBUG_CHANNEL(server->config.logger, channel, "Session " UA_PRINTF_GUID_FORMAT " created",
                          UA_PRINTF_GUID_DATA(newSession->sessionId.identifier.guid));
 }
@@ -60,74 +68,12 @@ Service_ActivateSession(UA_Server *server, UA_SecureChannel *channel,
         return;
     }
 
-    if(request->userIdentityToken.encoding < UA_EXTENSIONOBJECT_DECODED ||
-       (request->userIdentityToken.content.decoded.type != &UA_TYPES[UA_TYPES_ANONYMOUSIDENTITYTOKEN] &&
-        request->userIdentityToken.content.decoded.type != &UA_TYPES[UA_TYPES_USERNAMEIDENTITYTOKEN])) {
-        UA_LOG_INFO_SESSION(server->config.logger, session, "ActivateSession: SecureChannel %i wants "
-                            "to activate, but the UserIdentify token is invalid", channel->securityToken.channelId);
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADIDENTITYTOKENINVALID;
-        return;
-    }
-
-
-    UA_String ap = UA_STRING(ANONYMOUS_POLICY);
-    UA_String up = UA_STRING(USERNAME_POLICY);
-
-    /* Compatibility notice: Siemens OPC Scout v10 provides an empty policyId,
-       this is not okay For compatibility we will assume that empty policyId == ANONYMOUS_POLICY
-       if(token.policyId->data == NULL)
-           response->responseHeader.serviceResult = UA_STATUSCODE_BADIDENTITYTOKENINVALID;
-    */
-
-    if(server->config.enableAnonymousLogin &&
-       request->userIdentityToken.content.decoded.type == &UA_TYPES[UA_TYPES_ANONYMOUSIDENTITYTOKEN]) {
-        /* anonymous login */
-        const UA_AnonymousIdentityToken *token = request->userIdentityToken.content.decoded.data;
-        if(token->policyId.data && !UA_String_equal(&token->policyId, &ap)) {
-            response->responseHeader.serviceResult = UA_STATUSCODE_BADIDENTITYTOKENINVALID;
-            return;
-        }
-    } else if(server->config.enableUsernamePasswordLogin &&
-              request->userIdentityToken.content.decoded.type == &UA_TYPES[UA_TYPES_USERNAMEIDENTITYTOKEN]) {
-        /* username login */
-        const UA_UserNameIdentityToken *token = request->userIdentityToken.content.decoded.data;
-        if(!UA_String_equal(&token->policyId, &up)) {
-            response->responseHeader.serviceResult = UA_STATUSCODE_BADIDENTITYTOKENINVALID;
-            return;
-        }
-        if(token->encryptionAlgorithm.length > 0) {
-            /* we don't support encryption */
-            response->responseHeader.serviceResult = UA_STATUSCODE_BADIDENTITYTOKENINVALID;
-            return;
-        }
-
-        if(token->userName.length == 0 && token->password.length == 0) {
-            /* empty username and password */
-            response->responseHeader.serviceResult = UA_STATUSCODE_BADIDENTITYTOKENINVALID;
-            return;
-        }
-
-        /* trying to match pw/username */
-        UA_Boolean match = false;
-        for(size_t i = 0; i < server->config.usernamePasswordLoginsSize; i++) {
-            UA_String *user = &server->config.usernamePasswordLogins[i].username;
-            UA_String *pw = &server->config.usernamePasswordLogins[i].password;
-            if(UA_String_equal(&token->userName, user) && UA_String_equal(&token->password, pw)) {
-                match = true;
-                break;
-            }
-        }
-        if(!match) {
-            UA_LOG_INFO_SESSION(server->config.logger, session,
-                                "ActivateSession: Did not find matching username/password");
-            response->responseHeader.serviceResult = UA_STATUSCODE_BADUSERACCESSDENIED;
-            return;
-        }
-    } else {
-        /* Unsupported token type */
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADIDENTITYTOKENINVALID;
+    /* Callback into userland access control */
+    response->responseHeader.serviceResult =
+        server->config.accessControl.activateSession(&session->sessionId, &request->userIdentityToken,
+                                                     &session->sessionHandle);
+    if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD)
         return;
-    }
 
     /* Detach the old SecureChannel */
     if(session->channel && session->channel != channel) {
@@ -146,6 +92,8 @@ void
 Service_CloseSession(UA_Server *server, UA_Session *session, const UA_CloseSessionRequest *request,
                      UA_CloseSessionResponse *response) {
     UA_LOG_INFO_SESSION(server->config.logger, session, "CloseSession");
+    /* Callback into userland access control */
+    server->config.accessControl.closeSession(&session->sessionId, session->sessionHandle);
     response->responseHeader.serviceResult =
         UA_SessionManager_removeSession(&server->sessionManager, &session->authenticationToken);
 }

+ 2 - 1
src/ua_session.h

@@ -29,8 +29,9 @@ typedef struct UA_PublishResponseEntry {
 
 struct UA_Session {
     UA_ApplicationDescription clientDescription;
-    UA_Boolean        activated;
     UA_String         sessionName;
+    UA_Boolean        activated;
+    void             *sessionHandle; // pointer assigned in userland-callback
     UA_NodeId         authenticationToken;
     UA_NodeId         sessionId;
     UA_UInt32         maxRequestMessageSize;