Bladeren bron

Merge pull request #225 from acplt/server_config

Looks good!
Julius Pfrommer 10 jaren geleden
bovenliggende
commit
1d104f1668

+ 4 - 3
examples/server.c

@@ -6,7 +6,8 @@
 #include <signal.h>
 #include <errno.h> // errno, EINTR
 #include <stdio.h>
-#include <stdlib.h> 
+#include <stdlib.h>
+#include <string.h>
 #define __USE_XOPEN2K
 #ifdef UA_MULTITHREADING
 # include <pthread.h>
@@ -190,14 +191,14 @@ int main(int argc, char** argv) {
 	pthread_rwlock_init(&writeLock, 0);
 #endif
 
-	UA_Server *server = UA_Server_new();
+	UA_Server *server = UA_Server_new(UA_ServerConfig_standard);
 	logger = Logger_Stdout_new();
 	UA_Server_setLogger(server, logger);
     UA_ByteString certificate = loadCertificate();
     UA_Server_setServerCertificate(server, certificate);
     UA_ByteString_deleteMembers(&certificate);
 	UA_Server_addNetworkLayer(server, ServerNetworkLayerTCP_new(UA_ConnectionConfig_standard, 16664));
-    UA_UInt16 nsIndex = UA_Server_addNamespace(server, "urn:unconfigured:open62541:open62541Server");
+    UA_UInt16 nsIndex = UA_Server_addNamespace(server, UA_ServerConfig_standard.Application_applicationURI);
 
 	// print the status every 2 sec
 	UA_WorkItem work = {.type = UA_WORKITEMTYPE_METHODCALL,

+ 3 - 2
examples/server_simple.c

@@ -7,6 +7,7 @@
 #include <stdlib.h> 
 #include <signal.h>
 #include <errno.h> // errno, EINTR
+#include <string.h>
 
 #ifdef NOT_AMALGATED
     #include "ua_types.h"
@@ -59,14 +60,14 @@ static void testCallback(UA_Server *server, void *data) {
 int main(int argc, char** argv) {
 	signal(SIGINT, stopHandler); /* catches ctrl-c */
 
-	UA_Server *server = UA_Server_new();
+	UA_Server *server = UA_Server_new(UA_ServerConfig_standard);
     logger = Logger_Stdout_new();
     UA_Server_setLogger(server, logger);
     UA_ByteString certificate = loadCertificate();
     UA_Server_setServerCertificate(server, certificate);
     UA_ByteString_deleteMembers(&certificate);
     UA_Server_addNetworkLayer(server, ServerNetworkLayerTCP_new(UA_ConnectionConfig_standard, 16664));
-    UA_Server_addNamespace(server, "urn:unconfigured:open62541:open62541Server");
+    UA_Server_addNamespace(server, UA_ServerConfig_standard.Application_applicationURI);
 
     UA_WorkItem work = {.type = UA_WORKITEMTYPE_METHODCALL,
                         .work.methodCall = {.method = testCallback, .data = NULL} };

+ 14 - 1
include/ua_server.h

@@ -32,10 +32,23 @@ extern "C" {
  * @{
  */
 
+typedef struct UA_ServerConfig {
+    UA_Boolean  Login_enableAnonymous;
+
+    UA_Boolean  Login_enableUsernamePassword;
+    char**      Login_usernames;
+    char**      Login_passwords;
+    UA_UInt32   Login_loginsCount;
+
+    char*       Application_applicationURI;
+} UA_ServerConfig;
+
+extern const UA_ServerConfig UA_ServerConfig_standard;
+
 struct UA_Server;
 typedef struct UA_Server UA_Server;
 
-UA_Server UA_EXPORT * UA_Server_new(void);
+UA_Server UA_EXPORT * UA_Server_new(UA_ServerConfig config);
 void UA_EXPORT UA_Server_setServerCertificate(UA_Server *server, UA_ByteString certificate);
 void UA_EXPORT UA_Server_delete(UA_Server *server);
 

+ 4 - 1
include/ua_types.h

@@ -332,7 +332,7 @@ UA_TYPE_HANDLING_FUNCTIONS(UA_DiagnosticInfo)
     allocated. If the memory cannot be allocated, a null-string is returned. */
 UA_String UA_EXPORT UA_String_fromChars(char const *src);
 #define UA_STRING_ALLOC(CHARS) UA_String_fromChars(CHARS)
-#define UA_STRING(CHARS) (const UA_String) {sizeof(CHARS)-1, (UA_Byte*)CHARS }
+#define UA_STRING(CHARS) (const UA_String) {strlen(CHARS), (UA_Byte*)CHARS }
 #define UA_STRING_NULL (UA_String) {-1, (UA_Byte*)0 }
 
 /** Printf a char-array into a UA_String. Memory for the string data is allocated. */
@@ -341,6 +341,9 @@ UA_StatusCode UA_EXPORT UA_String_copyprintf(char const *fmt, UA_String *dst, ..
 /** Compares two strings */
 UA_Boolean UA_EXPORT UA_String_equal(const UA_String *string1, const UA_String *string2);
 
+/** Compares an UA String with a char array */
+UA_Boolean UA_EXPORT UA_String_equalchars(const UA_String *string1, char *charString);
+
 /* DateTime */
 /** Returns the current time */
 UA_DateTime UA_EXPORT UA_DateTime_now(void);

+ 42 - 15
src/server/ua_server.c

@@ -6,6 +6,18 @@
 #include "ua_services.h"
 #include "ua_nodeids.h"
 
+
+const UA_ServerConfig UA_ServerConfig_standard = {
+        UA_TRUE,
+
+        UA_TRUE,
+        (char *[]){"user"},
+        (char *[]){"password"},
+        1,
+
+        "urn:unconfigured:open62541:open62541Server"
+};
+
 /**********************/
 /* Namespace Handling */
 /**********************/
@@ -222,11 +234,14 @@ static void addVariableTypeNode_subtype(UA_Server *server, char* name, UA_UInt32
                       &UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
 }
 
-UA_Server * UA_Server_new(void) {
+UA_Server * UA_Server_new(UA_ServerConfig config) {
     UA_Server *server = UA_malloc(sizeof(UA_Server));
     if(!server)
         return UA_NULL;
 
+    //FIXME: config contains strings, for now its okay, but consider copying them aswell
+    server->config = config;
+
     LIST_INIT(&server->timedWork);
 #ifdef UA_MULTITHREADING
     rcu_init();
@@ -246,12 +261,10 @@ UA_Server * UA_Server_new(void) {
 
     UA_ByteString_init(&server->serverCertificate);
 
-#define PRODUCT_URI "http://open62541.org"
-#define APPLICATION_URI "urn:unconfigured:open62541:open62541Server"
     // mockup application description
     UA_ApplicationDescription_init(&server->description);
     server->description.productUri = UA_STRING_ALLOC(PRODUCT_URI);
-    server->description.applicationUri = UA_STRING_ALLOC(APPLICATION_URI);
+    server->description.applicationUri = UA_STRING_ALLOC(server->config.Application_applicationURI);
     server->description.discoveryUrlsSize = 0;
 
     server->description.applicationName = UA_LOCALIZEDTEXT_ALLOC("", "Unconfigured open62541 application");
@@ -272,16 +285,30 @@ UA_Server * UA_Server_new(void) {
         endpoint->securityPolicyUri = UA_STRING_ALLOC("http://opcfoundation.org/UA/SecurityPolicy#None");
         endpoint->transportProfileUri = UA_STRING_ALLOC("http://opcfoundation.org/UA-Profile/Transport/uatcp-uasc-uabinary");
 
-        endpoint->userIdentityTokensSize = 2;
-        endpoint->userIdentityTokens = UA_Array_new(&UA_TYPES[UA_TYPES_USERTOKENPOLICY], 2);
-
-        UA_UserTokenPolicy_init(&endpoint->userIdentityTokens[0]);
-        endpoint->userIdentityTokens[0].tokenType = UA_USERTOKENTYPE_ANONYMOUS;
-        endpoint->userIdentityTokens[0].policyId = UA_STRING_ALLOC("my-anonymous-policy"); // defined per server
-
-        UA_UserTokenPolicy_init(&endpoint->userIdentityTokens[1]);
-        endpoint->userIdentityTokens[1].tokenType = UA_USERTOKENTYPE_USERNAME;
-        endpoint->userIdentityTokens[1].policyId = UA_STRING_ALLOC("my-username-policy"); // defined per server
+        int size = 0;
+        if(server->config.Login_enableAnonymous){
+            size++;
+        }
+        if(server->config.Login_enableUsernamePassword){
+            size++;
+        }
+        endpoint->userIdentityTokensSize = size;
+        endpoint->userIdentityTokens = UA_Array_new(&UA_TYPES[UA_TYPES_USERTOKENPOLICY], size);
+
+        int currentIndex = 0;
+        if(server->config.Login_enableAnonymous){
+            UA_UserTokenPolicy_init(&endpoint->userIdentityTokens[currentIndex]);
+            endpoint->userIdentityTokens[currentIndex].tokenType = UA_USERTOKENTYPE_ANONYMOUS;
+            endpoint->userIdentityTokens[currentIndex].policyId = UA_STRING_ALLOC(ANONYMOUS_POLICY); // defined per server
+            currentIndex++;
+        }
+
+        if(server->config.Login_enableUsernamePassword){
+            UA_UserTokenPolicy_init(&endpoint->userIdentityTokens[currentIndex]);
+            endpoint->userIdentityTokens[currentIndex].tokenType = UA_USERTOKENTYPE_USERNAME;
+            endpoint->userIdentityTokens[currentIndex].policyId = UA_STRING_ALLOC(USERNAME_POLICY); // defined per server
+            currentIndex++;
+        }
 
         /* UA_String_copy(endpointUrl, &endpoint->endpointUrl); */
         /* /\* The standard says "the HostName specified in the Server Certificate is the */
@@ -727,7 +754,7 @@ UA_Server * UA_Server_new(void) {
    serverArray->value.variant.data = UA_Array_new(&UA_TYPES[UA_TYPES_STRING], 1);
    serverArray->value.variant.arrayLength = 1;
    serverArray->value.variant.type = &UA_TYPES[UA_TYPES_STRING];
-   *(UA_String *)serverArray->value.variant.data = UA_STRING_ALLOC(APPLICATION_URI);
+   *(UA_String *)serverArray->value.variant.data = UA_STRING_ALLOC(server->config.Application_applicationURI);
    serverArray->valueRank = 1;
    serverArray->minimumSamplingInterval = 1.0;
    serverArray->historizing = UA_FALSE;

+ 1 - 1
src/server/ua_server_binary.c

@@ -135,7 +135,7 @@ static void init_response_header(const UA_RequestHeader *p, UA_ResponseHeader *r
         UA_##TYPE##Response_init(&r);                                   \
         init_response_header(&p.requestHeader, &r.responseHeader);      \
         if(!clientSession)                                              \
-            r.responseHeader.serviceResult = UA_STATUSCODE_BADSESSIONIDINVALID; \
+            r.responseHeader.serviceResult = UA_STATUSCODE_BADSESSIONNOTACTIVATED; \
         else                                                            \
             Service_##TYPE(server, clientSession, &p, &r);              \
         UA_##TYPE##Request_deleteMembers(&p);                           \

+ 7 - 0
src/server/ua_server_internal.h

@@ -7,6 +7,10 @@
 #include "ua_securechannel_manager.h"
 #include "ua_nodestore.h"
 
+#define PRODUCT_URI "http://open62541.org"
+#define ANONYMOUS_POLICY "open62541-anonymous-policy"
+#define USERNAME_POLICY "open62541-username-policy"
+
 /** Mapping of namespace-id and url to an external nodestore. For namespaces
     that have no mapping defined, the internal nodestore is used by default. */
 typedef struct UA_ExternalNamespace {
@@ -59,6 +63,9 @@ struct UA_Server {
 
     UA_DateTime startTime;
     UA_DateTime buildDate;
+
+    /* Config */
+    UA_ServerConfig config;
 };
 
 void UA_Server_processBinaryMessage(UA_Server *server, UA_Connection *connection, UA_ByteString *msg);

+ 83 - 4
src/server/ua_services_session.c

@@ -38,6 +38,12 @@ void Service_CreateSession(UA_Server *server, UA_SecureChannel *channel,
     }
 }
 
+#ifdef RETURN
+#undef RETURN
+#endif
+#define RETURN  UA_UserIdentityToken_deleteMembers(&token); \
+                UA_UserNameIdentityToken_deleteMembers(&username_token); \
+                return
 void Service_ActivateSession(UA_Server *server,UA_SecureChannel *channel,
                              const UA_ActivateSessionRequest *request,
                              UA_ActivateSessionResponse *response) {
@@ -47,11 +53,84 @@ void Service_ActivateSession(UA_Server *server,UA_SecureChannel *channel,
                                         (const UA_NodeId*)&request->requestHeader.authenticationToken,
                                         &foundSession);
 
-	if(foundSession == UA_NULL)
+	if(foundSession == UA_NULL){
         response->responseHeader.serviceResult = UA_STATUSCODE_BADIDENTITYTOKENINVALID;
-    else
-        channel->session = foundSession;
+        return;
+	}
+
+
+
+    UA_UserIdentityToken token;
+    UA_UserIdentityToken_init(&token);
+    size_t offset = 0;
+    UA_UserIdentityToken_decodeBinary(&request->userIdentityToken.body, &offset, &token);
+
+    UA_UserNameIdentityToken username_token;
+    UA_UserNameIdentityToken_init(&username_token);
+
+    //check policies
+
+    if(token.policyId.data == UA_NULL){ //user identity token is NULL
+        response->responseHeader.serviceResult = UA_STATUSCODE_BADIDENTITYTOKENINVALID;
+        //todo cleanup session
+        RETURN;
+    }
+
+    //anonymous logins
+    if(!server->config.Login_enableAnonymous && UA_String_equalchars(&token.policyId, ANONYMOUS_POLICY)){
+        response->responseHeader.serviceResult = UA_STATUSCODE_BADIDENTITYTOKENINVALID;
+        UA_UserIdentityToken_deleteMembers(&token);
+        //todo cleanup session
+        RETURN;
+    }
+    //username logins
+    else if(UA_String_equalchars(&token.policyId, USERNAME_POLICY)){
+        if(!server->config.Login_enableUsernamePassword){
+            response->responseHeader.serviceResult = UA_STATUSCODE_BADIDENTITYTOKENINVALID;
+            //todo cleanup session
+            RETURN;
+        }
+        offset = 0;
+        UA_UserNameIdentityToken_decodeBinary(&request->userIdentityToken.body, &offset, &username_token);
+        if(username_token.encryptionAlgorithm.data != UA_NULL){
+            //we only support encryption
+            response->responseHeader.serviceResult = UA_STATUSCODE_BADIDENTITYTOKENINVALID;
+            //todo cleanup session
+            RETURN;
+        }
+        if(username_token.userName.length == -1 && username_token.password.length == -1){
+            //empty username and password
+            response->responseHeader.serviceResult = UA_STATUSCODE_BADIDENTITYTOKENINVALID;
+            //todo cleanup session
+            RETURN;
+        }
+        UA_Boolean matched = UA_FALSE;
+        for(UA_UInt32 i=0;i<server->config.Login_loginsCount;++i){
+            if(UA_String_equalchars(&username_token.userName, server->config.Login_usernames[i])
+            && UA_String_equalchars(&username_token.password, server->config.Login_passwords[i])){
+                matched = UA_TRUE;
+                break;
+            }
+        }
+        if(!matched){
+            //no username/pass matched
+            response->responseHeader.serviceResult = UA_STATUSCODE_BADUSERACCESSDENIED;
+            //todo cleanup session
+            RETURN;
+        }
+   }else{
+       response->responseHeader.serviceResult = UA_STATUSCODE_BADIDENTITYTOKENINVALID;
+       //todo cleanup session
+       RETURN;
+   }
+
+   //success - bind session to the channel
+   channel->session = foundSession;
+
+   RETURN;
+
 }
+#undef RETURN
 
 void Service_CloseSession(UA_Server *server, UA_Session *session, const UA_CloseSessionRequest *request,
                           UA_CloseSessionResponse *response) {
@@ -60,7 +139,7 @@ void Service_CloseSession(UA_Server *server, UA_Session *session, const UA_Close
 			(const UA_NodeId*)&request->requestHeader.authenticationToken, &foundSession);
 
 	if(foundSession == UA_NULL){
-		response->responseHeader.serviceResult = UA_STATUSCODE_BADIDENTITYTOKENINVALID;
+		response->responseHeader.serviceResult = UA_STATUSCODE_BADSESSIONIDINVALID;
 		return;
 	}
 

+ 5 - 0
src/ua_types.c

@@ -159,6 +159,11 @@ UA_Boolean UA_String_equal(const UA_String *string1, const UA_String *string2) {
     return (is == 0) ? UA_TRUE : UA_FALSE;
 }
 
+UA_Boolean UA_String_equalchars(const UA_String *string1, char *charString) {
+    UA_String string2 = UA_STRING(charString);
+    return UA_String_equal(string1, &string2);
+}
+
 /* DateTime */
 #define UNIX_EPOCH_BIAS_SEC 11644473600LL // Number of seconds from 1 Jan. 1601 00:00 to 1 Jan 1970 00:00 UTC
 #define HUNDRED_NANOSEC_PER_USEC 10LL

+ 1 - 1
tests/check_server_interaction_fileinput.c

@@ -26,7 +26,7 @@ static void readCallback(void) {
 }
 
 START_TEST(readVariable) {
-	UA_Server *server = UA_Server_new();
+	UA_Server *server = UA_Server_new(UA_ServerConfig_standard);
     UA_Server_setLogger(server, Logger_Stdout_new());
     UA_Server_addNetworkLayer(server, ServerNetworkLayerFileInput_new(max_reads, filenames, readCallback,
                                                                       writeCallback, NULL));

+ 1 - 1
tools/generate_datatypes.py

@@ -57,7 +57,7 @@ minimal_types = ["InvalidType", "Node", "NodeClass", "ReferenceNode", "Applicati
                  "NodeAttributes","ReferenceTypeAttributes", "ViewAttributes", "ObjectTypeAttributes",
                  "NodeAttributesMask","DeleteNodesItem", "DeleteNodesRequest", "DeleteNodesResponse",
                  "DeleteReferencesItem", "DeleteReferencesRequest", "DeleteReferencesResponse",
-                 "RegisterNodesRequest", "RegisterNodesResponse", "UnregisterNodesRequest", "UnregisterNodesResponse"]
+                 "RegisterNodesRequest", "RegisterNodesResponse", "UnregisterNodesRequest", "UnregisterNodesResponse", "UserIdentityToken", "UserNameIdentityToken"]
 
 class TypeDescription(object):
     def __init__(self, name, nodeid, namespaceid):