Parcourir la source

Merge pull request #2981 from open62541/merge_10

Merge 1.0 into master
Stefan Profanter il y a 5 ans
Parent
commit
6cca672c45

+ 4 - 0
.travis.yml

@@ -9,6 +9,10 @@ env:
 
 
 dist: trusty
 dist: trusty
 
 
+# Make sure we have the git tags. Travis uses shallow checkout where git describe does not work
+git:
+  depth: false
+
 matrix:
 matrix:
   fast_finish: true
   fast_finish: true
   include:
   include:

+ 6 - 16
CMakeLists.txt

@@ -32,22 +32,12 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
 # Version #
 # Version #
 ###########
 ###########
 
 
-set(OPEN62541_VER_MAJOR 1)
-set(OPEN62541_VER_MINOR 0)
-set(OPEN62541_VER_PATCH 0)
-set(OPEN62541_VER_LABEL "-dev") # Appended to the X.Y.Z version format. For example "-rc1" or an empty string
-
-# Set OPEN62541_VER_COMMIT
-if(GIT_FOUND)
-    execute_process(COMMAND ${GIT_EXECUTABLE} describe --always --tags
-                    RESULT_VARIABLE res_var OUTPUT_VARIABLE GIT_COM_ID WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
-    if(${res_var} EQUAL 0 AND NOT OPEN62541_VER_COMMIT STREQUAL "")
-        string(REPLACE "\n" "" OPEN62541_VER_COMMIT ${GIT_COM_ID} )
-    endif()
-endif()
-if(NOT OPEN62541_VER_COMMIT OR OPEN62541_VER_COMMIT STREQUAL "")
-    set(OPEN62541_VER_COMMIT "undefined")
-endif()
+include(SetGitBasedVersion)
+
+# The version string will be automatically set based on the git describe output.
+# This will automate the version strings. Just create a new tag, and the version will be set correctly.
+set_open62541_version()
+MESSAGE(STATUS "open62541 Version: ${OPEN62541_VER_MAJOR}.${OPEN62541_VER_MINOR}.${OPEN62541_VER_PATCH}${OPEN62541_VER_LABEL}")
 
 
 #################
 #################
 # Build Options #
 # Build Options #

+ 3 - 1
appveyor.yml

@@ -1,7 +1,9 @@
 version: '{build}'
 version: '{build}'
 
 
 clone_folder: c:\projects\open62541
 clone_folder: c:\projects\open62541
-clone_depth: 20
+
+# Do not set clone depth, we need all the tags to automatically detect the version based on git describe
+# clone_depth: 20
 
 
 # Avoid building branch if it is part of a PR and built anyways
 # Avoid building branch if it is part of a PR and built anyways
 skip_branch_with_pr: true
 skip_branch_with_pr: true

+ 48 - 0
debian/update_changelog.py

@@ -0,0 +1,48 @@
+#!/usr/bin/env python
+
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import subprocess
+import os
+import re
+from email.utils import formatdate
+
+git_describe_version = subprocess.check_output(["git", "describe", "--tags", "--dirty", "--match", "v*"]).decode('utf-8').strip()
+
+# v1.2
+# v1.2.3
+# v1.2.3-rc1
+# v1.2.3-rc1-dirty
+# v1.2.3-5-g4538abcd
+# v1.2.3-5-g4538abcd-dirty
+# git_describe_version = "v1.2.3"
+
+m = re.match(r"^v([0-9]+)(\.[0-9]+)?(\.[0-9]+)?(-(.*)+)?$", git_describe_version)
+version_major = m.group(1) if m.group(1) is not None else "0"
+version_minor = m.group(2).replace(".", "") if m.group(2) is not None else "0"
+version_patch = m.group(3).replace(".", "") if m.group(3) is not None else "0"
+version_label = m.group(4) if m.group(4) is not None else ""
+#print("major {} minor {} patch {} label {}".format(version_major, version_minor, version_patch, version_label))
+
+debian_distribution = "unstable"
+if version_label is not "":
+    debian_distribution = "UNRELEASED"
+
+dirpath = os.path.dirname(os.path.realpath(__file__))
+changelog_file = os.path.join(dirpath, "changelog")
+
+# remove leading 'v'
+changelog_version = git_describe_version[1:] if git_describe_version[0] == 'v' else git_describe_version
+
+with open(changelog_file, 'r') as original: data = original.read()
+with open(changelog_file, 'w') as modified:
+    new_entry = """open62541 ({version}) {distribution}; urgency=medium
+
+  * Full changelog is available here: https://github.com/open62541/open62541/blob/master/CHANGELOG
+
+ -- open62541 Team <open62541-core@googlegroups.com>  {time}
+""".format(version=changelog_version, time=formatdate(), distribution = debian_distribution)
+
+    modified.write(new_entry + "\n" + data)

+ 84 - 9
examples/server_ctt.c

@@ -9,9 +9,10 @@
 #define _CRT_SECURE_NO_WARNINGS /* disable fopen deprication warning in msvs */
 #define _CRT_SECURE_NO_WARNINGS /* disable fopen deprication warning in msvs */
 #endif
 #endif
 
 
-#include <open62541/plugin/log_stdout.h>
 #include <open62541/server.h>
 #include <open62541/server.h>
+#include <open62541/plugin/log_stdout.h>
 #include <open62541/server_config_default.h>
 #include <open62541/server_config_default.h>
+#include <open62541/plugin/pki_default.h>
 
 
 #include <signal.h>
 #include <signal.h>
 #include <stdlib.h>
 #include <stdlib.h>
@@ -828,7 +829,9 @@ disableOutdatedSecurityPolicy(UA_ServerConfig *config) {
     for(size_t i = 0; i < config->endpointsSize; i++) {
     for(size_t i = 0; i < config->endpointsSize; i++) {
         UA_EndpointDescription *ep = &config->endpoints[i];
         UA_EndpointDescription *ep = &config->endpoints[i];
         UA_ByteString basic128uri = UA_BYTESTRING("http://opcfoundation.org/UA/SecurityPolicy#Basic128Rsa15");
         UA_ByteString basic128uri = UA_BYTESTRING("http://opcfoundation.org/UA/SecurityPolicy#Basic128Rsa15");
-        if(!UA_String_equal(&ep->securityPolicyUri, &basic128uri))
+        UA_ByteString basic256uri = UA_BYTESTRING("http://opcfoundation.org/UA/SecurityPolicy#Basic256");
+        if(!UA_String_equal(&ep->securityPolicyUri, &basic128uri) &&
+           !UA_String_equal(&ep->securityPolicyUri, &basic256uri))
             continue;
             continue;
 
 
         UA_EndpointDescription_clear(ep);
         UA_EndpointDescription_clear(ep);
@@ -934,9 +937,15 @@ usage(void) {
                    "server_ctt [<server-certificate.der>]\n"
                    "server_ctt [<server-certificate.der>]\n"
 #else
 #else
                    "server_ctt <server-certificate.der> <private-key.der>\n"
                    "server_ctt <server-certificate.der> <private-key.der>\n"
+#ifndef __linux__
                    "\t[--trustlist <tl1.ctl> <tl2.ctl> ... ]\n"
                    "\t[--trustlist <tl1.ctl> <tl2.ctl> ... ]\n"
                    "\t[--issuerlist <il1.der> <il2.der> ... ]\n"
                    "\t[--issuerlist <il1.der> <il2.der> ... ]\n"
                    "\t[--revocationlist <rv1.crl> <rv2.crl> ...]\n"
                    "\t[--revocationlist <rv1.crl> <rv2.crl> ...]\n"
+#else
+                   "\t[--trustlistFolder <folder>]\n"
+                   "\t[--issuerlistFolder <folder>]\n"
+                   "\t[--revocationlistFolder <folder>]\n"
+#endif
                    "\t[--enableUnencrypted]\n"
                    "\t[--enableUnencrypted]\n"
                    "\t[--enableOutdatedSecurityPolicy]\n"
                    "\t[--enableOutdatedSecurityPolicy]\n"
                    "\t[--enableTimestampCheck]\n"
                    "\t[--enableTimestampCheck]\n"
@@ -988,12 +997,6 @@ int main(int argc, char **argv) {
         pos++;
         pos++;
     }
     }
 
 
-    UA_ByteString trustList[100];
-    size_t trustListSize = 0;
-    UA_ByteString issuerList[100];
-    size_t issuerListSize = 0;
-    UA_ByteString revocationList[100];
-    size_t revocationListSize = 0;
     char filetype = ' '; /* t==trustlist, l == issuerList, r==revocationlist */
     char filetype = ' '; /* t==trustlist, l == issuerList, r==revocationlist */
     UA_Boolean enableUnencr = false;
     UA_Boolean enableUnencr = false;
     UA_Boolean enableSec = false;
     UA_Boolean enableSec = false;
@@ -1002,6 +1005,19 @@ int main(int argc, char **argv) {
     UA_Boolean disableBasic256 = false;
     UA_Boolean disableBasic256 = false;
     UA_Boolean disableBasic256Sha256 = false;
     UA_Boolean disableBasic256Sha256 = false;
 
 
+#ifndef __linux__
+    UA_ByteString trustList[100];
+    size_t trustListSize = 0;
+    UA_ByteString issuerList[100];
+    size_t issuerListSize = 0;
+    UA_ByteString revocationList[100];
+    size_t revocationListSize = 0;
+#else
+    const char *trustlistFolder = NULL;
+    const char *issuerlistFolder = NULL;
+    const char *revocationlistFolder = NULL;
+#endif
+
 #endif
 #endif
 
 
     UA_Boolean enableAnon = false;
     UA_Boolean enableAnon = false;
@@ -1045,6 +1061,22 @@ int main(int argc, char **argv) {
             continue;
             continue;
         }
         }
 
 
+        if(strcmp(argv[pos], "--disableBasic128") == 0) {
+            disableBasic128 = true;
+            continue;
+        }
+
+        if(strcmp(argv[pos], "--disableBasic256") == 0) {
+            disableBasic256 = true;
+            continue;
+        }
+
+        if(strcmp(argv[pos], "--disableBasic256Sha256") == 0) {
+            disableBasic256Sha256 = true;
+            continue;
+        }
+
+#ifndef __linux__
         if(strcmp(argv[pos], "--trustlist") == 0) {
         if(strcmp(argv[pos], "--trustlist") == 0) {
             filetype = 't';
             filetype = 't';
             continue;
             continue;
@@ -1107,6 +1139,38 @@ int main(int argc, char **argv) {
             revocationListSize++;
             revocationListSize++;
             continue;
             continue;
         }
         }
+#else
+        if(strcmp(argv[pos], "--trustlistFolder") == 0) {
+            filetype = 't';
+            continue;
+        }
+
+        if(strcmp(argv[pos], "--issuerlistFolder") == 0) {
+            filetype = 'l';
+            continue;
+        }
+
+        if(strcmp(argv[pos], "--revocationlistFolder") == 0) {
+            filetype = 'r';
+            continue;
+        }
+
+        if(filetype == 't') {
+            trustlistFolder = argv[pos];
+            continue;
+        }
+
+        if(filetype == 'l') {
+            issuerlistFolder = argv[pos];
+            continue;
+        }
+
+        if(filetype == 'r') {
+            revocationlistFolder = argv[pos];
+            continue;
+        }
+#endif
+
 #endif
 #endif
 
 
         usage();
         usage();
@@ -1114,11 +1178,22 @@ int main(int argc, char **argv) {
     }
     }
 
 
 #ifdef UA_ENABLE_ENCRYPTION
 #ifdef UA_ENABLE_ENCRYPTION
+#ifndef __linux__
     UA_ServerConfig_setDefaultWithSecurityPolicies(&config, 4840,
     UA_ServerConfig_setDefaultWithSecurityPolicies(&config, 4840,
                                                    &certificate, &privateKey,
                                                    &certificate, &privateKey,
                                                    trustList, trustListSize,
                                                    trustList, trustListSize,
                                                    issuerList, issuerListSize,
                                                    issuerList, issuerListSize,
                                                    revocationList, revocationListSize);
                                                    revocationList, revocationListSize);
+#else
+    UA_ServerConfig_setDefaultWithSecurityPolicies(&config, 4840,
+                                                   &certificate, &privateKey,
+                                                   NULL, 0, NULL, 0, NULL, 0);
+    config.certificateVerification.deleteMembers(&config.certificateVerification);
+    UA_CertificateVerification_CertFolders(&config.certificateVerification,
+                                           trustlistFolder, issuerlistFolder,
+                                           revocationlistFolder);
+#endif
+
     if(!enableUnencr)
     if(!enableUnencr)
         disableUnencrypted(&config);
         disableUnencrypted(&config);
     if(!enableSec)
     if(!enableSec)
@@ -1156,7 +1231,7 @@ int main(int argc, char **argv) {
 
 
     /* Clean up temp values */
     /* Clean up temp values */
     UA_ByteString_clear(&certificate);
     UA_ByteString_clear(&certificate);
-#ifdef UA_ENABLE_ENCRYPTION
+#if defined(UA_ENABLE_ENCRYPTION) && !defined(__linux__)
     UA_ByteString_clear(&privateKey);
     UA_ByteString_clear(&privateKey);
     for(size_t i = 0; i < trustListSize; i++)
     for(size_t i = 0; i < trustListSize; i++)
         UA_ByteString_clear(&trustList[i]);
         UA_ByteString_clear(&trustList[i]);

+ 1 - 1
include/open62541/server.h

@@ -467,7 +467,7 @@ UA_Server_browseNext(UA_Server *server, UA_Boolean releaseContinuationPoint,
  * by adding every target node at most once to the results array. */
  * by adding every target node at most once to the results array. */
 UA_StatusCode UA_EXPORT
 UA_StatusCode UA_EXPORT
 UA_Server_browseRecursive(UA_Server *server, const UA_BrowseDescription *bd,
 UA_Server_browseRecursive(UA_Server *server, const UA_BrowseDescription *bd,
-                          size_t *resultsSize, UA_ExpandedNodeId *results);
+                          size_t *resultsSize, UA_ExpandedNodeId **results);
 
 
 UA_BrowsePathResult UA_EXPORT
 UA_BrowsePathResult UA_EXPORT
 UA_Server_translateBrowsePathToNodeIds(UA_Server *server,
 UA_Server_translateBrowsePathToNodeIds(UA_Server *server,

+ 8 - 0
plugins/include/open62541/plugin/pki_default.h

@@ -29,6 +29,14 @@ UA_CertificateVerification_Trustlist(UA_CertificateVerification *cv,
                                      const UA_ByteString *certificateRevocationList,
                                      const UA_ByteString *certificateRevocationList,
                                      size_t certificateRevocationListSize);
                                      size_t certificateRevocationListSize);
 
 
+#if __linux__ /* Linux only so far */
+UA_EXPORT UA_StatusCode
+UA_CertificateVerification_CertFolders(UA_CertificateVerification *cv,
+                                       const char *trustListFolder,
+                                       const char *issuerListFolder,
+                                       const char *revocationListFolder);
+#endif
+
 #endif
 #endif
 
 
 _UA_END_DECLS
 _UA_END_DECLS

+ 9 - 9
plugins/ua_config_default.c

@@ -232,19 +232,19 @@ addDefaultNetworkLayers(UA_ServerConfig *conf, UA_UInt16 portNumber,
 
 
 static UA_StatusCode
 static UA_StatusCode
 addDiscoveryUrl(UA_ServerConfig *config, UA_UInt16 portNumber) {
 addDiscoveryUrl(UA_ServerConfig *config, UA_UInt16 portNumber) {
-       config->applicationDescription.discoveryUrlsSize = 1;
+    config->applicationDescription.discoveryUrlsSize = 1;
     UA_String *discurl = (UA_String *) UA_Array_new(1, &UA_TYPES[UA_TYPES_STRING]);
     UA_String *discurl = (UA_String *) UA_Array_new(1, &UA_TYPES[UA_TYPES_STRING]);
     char discoveryUrlBuffer[220];
     char discoveryUrlBuffer[220];
     if (config->customHostname.length) {
     if (config->customHostname.length) {
         UA_snprintf(discoveryUrlBuffer, 220, "opc.tcp://%.*s:%d/",
         UA_snprintf(discoveryUrlBuffer, 220, "opc.tcp://%.*s:%d/",
-                                                 (int)config->customHostname.length,
-                                                 config->customHostname.data,
-                                                 portNumber);
+                    (int)config->customHostname.length,
+                    config->customHostname.data,
+                    portNumber);
     } else {
     } else {
-    char hostnameBuffer[200];
-       if(UA_gethostname(hostnameBuffer, 200) == 0) {
-               UA_snprintf(discoveryUrlBuffer, 220, "opc.tcp://%s:%d/", hostnameBuffer, portNumber);
-       } else {
+        char hostnameBuffer[200];
+        if(UA_gethostname(hostnameBuffer, 200) == 0) {
+            UA_snprintf(discoveryUrlBuffer, 220, "opc.tcp://%s:%d/", hostnameBuffer, portNumber);
+        } else {
             UA_LOG_ERROR(&config->logger, UA_LOGCATEGORY_NETWORK, "Could not get the hostname");
             UA_LOG_ERROR(&config->logger, UA_LOGCATEGORY_NETWORK, "Could not get the hostname");
         }
         }
     }
     }
@@ -423,7 +423,7 @@ UA_ServerConfig_setMinimalCustomBuffer(UA_ServerConfig *config, UA_UInt16 portNu
         UA_ServerConfig_clean(config);
         UA_ServerConfig_clean(config);
         return retval;
         return retval;
     }
     }
-    
+
     retval = addDiscoveryUrl(config, portNumber);
     retval = addDiscoveryUrl(config, portNumber);
     if (retval != UA_STATUSCODE_GOOD) {
     if (retval != UA_STATUSCODE_GOOD) {
         UA_ServerConfig_clean(config);
         UA_ServerConfig_clean(config);

+ 184 - 0
plugins/ua_pki_default.c

@@ -3,13 +3,17 @@
  *
  *
  *    Copyright 2018 (c) Mark Giraud, Fraunhofer IOSB
  *    Copyright 2018 (c) Mark Giraud, Fraunhofer IOSB
  *    Copyright 2019 (c) Kalycito Infotech Private Limited
  *    Copyright 2019 (c) Kalycito Infotech Private Limited
+ *    Copyright 2019 (c) Julius Pfrommer, Fraunhofer IOSB
  */
  */
 
 
+#include <open62541/server_config.h>
 #include <open62541/plugin/pki_default.h>
 #include <open62541/plugin/pki_default.h>
+#include <open62541/plugin/log_stdout.h>
 
 
 #ifdef UA_ENABLE_ENCRYPTION
 #ifdef UA_ENABLE_ENCRYPTION
 #include <mbedtls/x509.h>
 #include <mbedtls/x509.h>
 #include <mbedtls/x509_crt.h>
 #include <mbedtls/x509_crt.h>
+#include <mbedtls/error.h>
 #endif
 #endif
 
 
 #define REMOTECERTIFICATETRUSTED 1
 #define REMOTECERTIFICATETRUSTED 1
@@ -48,11 +52,143 @@ void UA_CertificateVerification_AcceptAll(UA_CertificateVerification *cv) {
 #ifdef UA_ENABLE_ENCRYPTION
 #ifdef UA_ENABLE_ENCRYPTION
 
 
 typedef struct {
 typedef struct {
+    /* If the folders are defined, we use them to reload the certificates during
+     * runtime */
+    UA_String trustListFolder;
+    UA_String issuerListFolder;
+    UA_String revocationListFolder;
+
     mbedtls_x509_crt certificateTrustList;
     mbedtls_x509_crt certificateTrustList;
     mbedtls_x509_crt certificateIssuerList;
     mbedtls_x509_crt certificateIssuerList;
     mbedtls_x509_crl certificateRevocationList;
     mbedtls_x509_crl certificateRevocationList;
 } CertInfo;
 } CertInfo;
 
 
+#if __linux__ /* Linux only so far */
+
+#include <dirent.h>
+#include <limits.h>
+
+static UA_StatusCode
+fileNamesFromFolder(const UA_String *folder, size_t *pathsSize, UA_String **paths) {
+    char buf[PATH_MAX + 1];
+    if(folder->length > PATH_MAX)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    memcpy(buf, folder->data, folder->length);
+    buf[folder->length] = 0;
+    
+    DIR *dir = opendir(buf);
+    if(!dir)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    *paths = (UA_String*)UA_Array_new(256, &UA_TYPES[UA_TYPES_STRING]);
+    if(*paths == NULL) {
+        closedir(dir);
+        return UA_STATUSCODE_BADOUTOFMEMORY;
+    }
+
+    struct dirent *ent;
+    char buf2[PATH_MAX + 1];
+    realpath(buf, buf2);
+    size_t pathlen = strlen(buf2);
+    *pathsSize = 0;
+    while((ent = readdir (dir)) != NULL && *pathsSize < 256) {
+        if(ent->d_type != DT_REG)
+            continue;
+        buf2[pathlen] = '/';
+        buf2[pathlen+1] = 0;
+        strcat(buf2, ent->d_name);
+        (*paths)[*pathsSize] = UA_STRING_ALLOC(buf2);
+        *pathsSize += 1;
+    }
+    closedir(dir);
+
+    if(*pathsSize == 0) {
+        UA_free(*paths);
+        *paths = NULL;
+    }
+    return UA_STATUSCODE_GOOD;
+}
+
+static UA_StatusCode
+reloadCertificates(CertInfo *ci) {
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+    int err = 0;
+
+    /* Load the trustlists */
+    if(ci->trustListFolder.length > 0) {
+        UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Reloading the trust-list");
+        mbedtls_x509_crt_free(&ci->certificateTrustList);
+        mbedtls_x509_crt_init(&ci->certificateTrustList);
+
+        char f[PATH_MAX];
+        memcpy(f, ci->trustListFolder.data, ci->trustListFolder.length);
+        f[ci->trustListFolder.length] = 0;
+        err = mbedtls_x509_crt_parse_path(&ci->certificateTrustList, f);
+        if(err == 0) {
+            UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER,
+                        "Loaded certificate from %s", f);
+        } else {
+            char errBuff[300];
+            mbedtls_strerror(err, errBuff, 300);
+            UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER,
+                        "Failed to load certificate from %s", f);
+        }
+    }
+
+    /* Load the revocationlists */
+    if(ci->revocationListFolder.length > 0) {
+        UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Reloading the revocation-list");
+        size_t pathsSize = 0;
+        UA_String *paths = NULL;
+        retval = fileNamesFromFolder(&ci->revocationListFolder, &pathsSize, &paths);
+        if(retval != UA_STATUSCODE_GOOD)
+            return retval;
+        mbedtls_x509_crl_free(&ci->certificateRevocationList);
+        mbedtls_x509_crl_init(&ci->certificateRevocationList);
+        for(size_t i = 0; i < pathsSize; i++) {
+            char f[PATH_MAX];
+            memcpy(f, paths[i].data, paths[i].length);
+            f[paths[i].length] = 0;
+            err = mbedtls_x509_crl_parse_file(&ci->certificateRevocationList, f);
+            if(err == 0) {
+                UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER,
+                            "Loaded certificate from %.*s",
+                            (int)paths[i].length, paths[i].data);
+            } else {
+                UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER,
+                            "Failed to load certificate from %.*s",
+                            (int)paths[i].length, paths[i].data);
+            }
+        }
+        UA_Array_delete(paths, pathsSize, &UA_TYPES[UA_TYPES_STRING]);
+        paths = NULL;
+        pathsSize = 0;
+    }
+
+    /* Load the issuerlists */
+    if(ci->issuerListFolder.length > 0) {
+        UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Reloading the issuer-list");
+        mbedtls_x509_crt_free(&ci->certificateIssuerList);
+        mbedtls_x509_crt_init(&ci->certificateIssuerList);
+        char f[PATH_MAX];
+        memcpy(f, ci->issuerListFolder.data, ci->issuerListFolder.length);
+        f[ci->issuerListFolder.length] = 0;
+        err = mbedtls_x509_crt_parse_path(&ci->certificateIssuerList, f);
+        if(err == 0) {
+            UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER,
+                        "Loaded certificate from %s", f);
+        } else {
+            UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER,
+                        "Failed to load certificate from %s", f);
+        }
+    }
+
+    return retval;
+}
+
+#endif
+
 static UA_StatusCode
 static UA_StatusCode
 certificateVerification_verify(void *verificationContext,
 certificateVerification_verify(void *verificationContext,
                                const UA_ByteString *certificate) {
                                const UA_ByteString *certificate) {
@@ -60,6 +196,16 @@ certificateVerification_verify(void *verificationContext,
     if(!ci)
     if(!ci)
         return UA_STATUSCODE_BADINTERNALERROR;
         return UA_STATUSCODE_BADINTERNALERROR;
 
 
+#if __linux__ /* Reload certificates if folder paths are specified */
+    reloadCertificates(ci);
+#endif
+
+    /* if(ci->certificateTrustList.raw.len == 0) { */
+    /*     UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, */
+    /*                    "No Trustlist loaded. Accepting the certificate."); */
+    /*     return UA_STATUSCODE_GOOD; */
+    /* } */
+
     /* Parse the certificate */
     /* Parse the certificate */
     mbedtls_x509_crt remoteCertificate;
     mbedtls_x509_crt remoteCertificate;
 
 
@@ -346,6 +492,10 @@ certificateVerification_deleteMembers(UA_CertificateVerification *cv) {
         return;
         return;
     mbedtls_x509_crt_free(&ci->certificateTrustList);
     mbedtls_x509_crt_free(&ci->certificateTrustList);
     mbedtls_x509_crl_free(&ci->certificateRevocationList);
     mbedtls_x509_crl_free(&ci->certificateRevocationList);
+    mbedtls_x509_crt_free(&ci->certificateIssuerList);
+    UA_String_clear(&ci->trustListFolder);
+    UA_String_clear(&ci->issuerListFolder);
+    UA_String_clear(&ci->revocationListFolder);
     UA_free(ci);
     UA_free(ci);
     cv->context = NULL;
     cv->context = NULL;
 }
 }
@@ -361,6 +511,7 @@ UA_CertificateVerification_Trustlist(UA_CertificateVerification *cv,
     CertInfo *ci = (CertInfo*)UA_malloc(sizeof(CertInfo));
     CertInfo *ci = (CertInfo*)UA_malloc(sizeof(CertInfo));
     if(!ci)
     if(!ci)
         return UA_STATUSCODE_BADOUTOFMEMORY;
         return UA_STATUSCODE_BADOUTOFMEMORY;
+    memset(ci, 0, sizeof(CertInfo));
     mbedtls_x509_crt_init(&ci->certificateTrustList);
     mbedtls_x509_crt_init(&ci->certificateTrustList);
     mbedtls_x509_crl_init(&ci->certificateRevocationList);
     mbedtls_x509_crl_init(&ci->certificateRevocationList);
     mbedtls_x509_crt_init(&ci->certificateIssuerList);
     mbedtls_x509_crt_init(&ci->certificateIssuerList);
@@ -402,4 +553,37 @@ error:
     return UA_STATUSCODE_BADINTERNALERROR;
     return UA_STATUSCODE_BADINTERNALERROR;
 }
 }
 
 
+#if __linux__ /* Linux only so far */
+
+UA_StatusCode
+UA_CertificateVerification_CertFolders(UA_CertificateVerification *cv,
+                                       const char *trustListFolder,
+                                       const char *issuerListFolder,
+                                       const char *revocationListFolder) {
+    CertInfo *ci = (CertInfo*)UA_malloc(sizeof(CertInfo));
+    if(!ci)
+        return UA_STATUSCODE_BADOUTOFMEMORY;
+    memset(ci, 0, sizeof(CertInfo));
+    mbedtls_x509_crt_init(&ci->certificateTrustList);
+    mbedtls_x509_crl_init(&ci->certificateRevocationList);
+    mbedtls_x509_crt_init(&ci->certificateIssuerList);
+
+    /* Only set the folder paths. They will be reloaded during runtime.
+     * TODO: Add a more efficient reloading of only the changes */
+    ci->trustListFolder = UA_STRING_ALLOC(trustListFolder);
+    ci->issuerListFolder = UA_STRING_ALLOC(issuerListFolder);
+    ci->revocationListFolder = UA_STRING_ALLOC(revocationListFolder);
+
+    reloadCertificates(ci);
+
+    cv->context = (void*)ci;
+    cv->verifyCertificate = certificateVerification_verify;
+    cv->deleteMembers = certificateVerification_deleteMembers;
+    cv->verifyApplicationURI = certificateVerification_verifyApplicationURI;
+
+    return UA_STATUSCODE_GOOD;
+}
+
+#endif
+
 #endif
 #endif

+ 2 - 19
src/server/ua_nodes.c

@@ -318,26 +318,9 @@ copyCommonVariableAttributes(UA_VariableNode *node,
     node->valueRank = attr->valueRank;
     node->valueRank = attr->valueRank;
 
 
     /* Copy the value */
     /* Copy the value */
-    node->valueSource = UA_VALUESOURCE_DATA;
-    UA_NodeId extensionObject = UA_NODEID_NUMERIC(0, UA_NS0ID_STRUCTURE);
-    /* If we have an extension object which is still encoded (e.g. from the
-     * nodeset compiler) return an error.
-     * This was used in the old version of the nodeset compiler and is not
-     * needed anymore. */
-    if(attr->value.type != NULL && UA_NodeId_equal(&attr->value.type->typeId, &extensionObject)) {
-        /* Do nothing since we got an empty array of extension objects */
-        if(attr->value.data == UA_EMPTY_ARRAY_SENTINEL)
-            return UA_STATUSCODE_GOOD;
-
-        const UA_ExtensionObject *obj = (const UA_ExtensionObject *)attr->value.data;
-        if(obj && obj->encoding == UA_EXTENSIONOBJECT_ENCODED_BYTESTRING) {
-            return UA_STATUSCODE_BADNOTSUPPORTED;
-        }
-    }
-
     retval = UA_Variant_copy(&attr->value, &node->value.data.value.value);
     retval = UA_Variant_copy(&attr->value, &node->value.data.value.value);
-
-    node->value.data.value.hasValue = node->value.data.value.value.type != NULL;
+    node->valueSource = UA_VALUESOURCE_DATA;
+    node->value.data.value.hasValue = (node->value.data.value.value.type != NULL);
 
 
     return retval;
     return retval;
 }
 }

+ 4 - 2
src/server/ua_server_ns0.c

@@ -899,11 +899,13 @@ UA_Server_initNS0(UA_Server *server) {
                                &maxBrowseContinuationPoints, &UA_TYPES[UA_TYPES_UINT16]);
                                &maxBrowseContinuationPoints, &UA_TYPES[UA_TYPES_UINT16]);
 
 
     /* ServerProfileArray */
     /* ServerProfileArray */
-    UA_String profileArray[2];
+    UA_String profileArray[3];
     UA_UInt16 profileArraySize = 0;
     UA_UInt16 profileArraySize = 0;
 #define ADDPROFILEARRAY(x) profileArray[profileArraySize++] = UA_STRING(x)
 #define ADDPROFILEARRAY(x) profileArray[profileArraySize++] = UA_STRING(x)
     ADDPROFILEARRAY("http://opcfoundation.org/UA-Profile/Server/MicroEmbeddedDevice");
     ADDPROFILEARRAY("http://opcfoundation.org/UA-Profile/Server/MicroEmbeddedDevice");
-
+#ifdef UA_ENABLE_NODEMANAGEMENT
+    ADDPROFILEARRAY("http://opcfoundation.org/UA-Profile/Server/NodeManagement");
+#endif
 #ifdef UA_ENABLE_METHODCALLS
 #ifdef UA_ENABLE_METHODCALLS
     ADDPROFILEARRAY("http://opcfoundation.org/UA-Profile/Server/Methods");
     ADDPROFILEARRAY("http://opcfoundation.org/UA-Profile/Server/Methods");
 #endif
 #endif

+ 8 - 0
src/server/ua_services_nodemanagement.c

@@ -923,6 +923,14 @@ recursiveTypeCheckAddChildren(UA_Server *server, UA_Session *session,
             return retval;
             return retval;
         }
         }
 
 
+        /* Check NodeClass for 'hasSubtype'. UA_NODECLASS_VARIABLE not allowed to have subtype */
+        if((node->nodeClass == UA_NODECLASS_VARIABLE) && (UA_NodeId_equal(
+                &node->references->referenceTypeId, &hasSubtype))) {
+            UA_LOG_INFO_SESSION(&server->config.logger, session,
+                                            "AddNodes: VariableType not allowed to have HasSubType");
+            return UA_STATUSCODE_BADREFERENCENOTALLOWED;
+        }
+
         /* Check if all attributes hold the constraints of the type now. The initial
         /* Check if all attributes hold the constraints of the type now. The initial
          * attributes must type-check. The constructor might change the attributes
          * attributes must type-check. The constructor might change the attributes
          * again. Then, the changes are type-checked by the normal write service. */
          * again. Then, the changes are type-checked by the normal write service. */

+ 2 - 2
src/server/ua_services_view.c

@@ -287,7 +287,7 @@ referenceSubtypes(UA_Server *server, const UA_NodeId *refType,
 
 
 UA_StatusCode
 UA_StatusCode
 UA_Server_browseRecursive(UA_Server *server, const UA_BrowseDescription *bd,
 UA_Server_browseRecursive(UA_Server *server, const UA_BrowseDescription *bd,
-                          size_t *resultsSize, UA_ExpandedNodeId *results) {
+                          size_t *resultsSize, UA_ExpandedNodeId **results) {
     /* Set the list of relevant reference types */
     /* Set the list of relevant reference types */
     UA_NodeId *refTypes = NULL;
     UA_NodeId *refTypes = NULL;
     size_t refTypesSize = 0;
     size_t refTypesSize = 0;
@@ -306,7 +306,7 @@ UA_Server_browseRecursive(UA_Server *server, const UA_BrowseDescription *bd,
 
 
     /* Browse */
     /* Browse */
     retval = browseRecursive(server, 1, &bd->nodeId, refTypesSize, refTypes,
     retval = browseRecursive(server, 1, &bd->nodeId, refTypesSize, refTypes,
-                             bd->browseDirection, false, resultsSize, &results);
+                             bd->browseDirection, false, resultsSize, results);
 
 
     /* Clean up */
     /* Clean up */
     if(refTypes && bd->includeSubtypes)
     if(refTypes && bd->includeSubtypes)

+ 28 - 0
tests/server/check_services_nodemanagement.c

@@ -85,6 +85,33 @@ START_TEST(AddVariableNode_Matrix) {
     ck_assert_int_eq(UA_STATUSCODE_GOOD, res);
     ck_assert_int_eq(UA_STATUSCODE_GOOD, res);
 } END_TEST
 } END_TEST
 
 
+START_TEST(AddVariableNode_ExtensionObject) {
+        /* Add a variable node to the address space */
+        UA_VariableAttributes attr = UA_VariableAttributes_default;
+        attr.displayName = UA_LOCALIZEDTEXT("en-US","the extensionobject");
+
+        /* Set an ExtensionObject with an unknown binary encoding */
+        UA_ExtensionObject myExtensionObject;
+        UA_ExtensionObject_init(&myExtensionObject);
+        myExtensionObject.encoding = UA_EXTENSIONOBJECT_ENCODED_BYTESTRING;
+        myExtensionObject.content.encoded.typeId = UA_NODEID_NUMERIC(5, 1234);
+        UA_ByteString byteString = UA_BYTESTRING("String Payload as a ByteString extension");
+        myExtensionObject.content.encoded.body = byteString;
+        UA_Variant_setScalar(&attr.value, &myExtensionObject, &UA_TYPES[UA_TYPES_EXTENSIONOBJECT]);
+
+        UA_NodeId myEONodeId = UA_NODEID_STRING(1, "the.extensionobject");
+        UA_QualifiedName myEOName = UA_QUALIFIEDNAME(1, "the extensionobject");
+        UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
+        UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
+        UA_StatusCode res =
+            UA_Server_addVariableNode(server, myEONodeId, parentNodeId,
+                                      parentReferenceNodeId, myEOName,
+                                      UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),
+                                      attr, NULL, NULL);
+        ck_assert_int_eq(UA_STATUSCODE_GOOD, res);
+    } END_TEST
+
+
 static UA_NodeId pointTypeId;
 static UA_NodeId pointTypeId;
 
 
 static void
 static void
@@ -594,6 +621,7 @@ int main(void) {
     tcase_add_checked_fixture(tc_addnodes, setup, teardown);
     tcase_add_checked_fixture(tc_addnodes, setup, teardown);
     tcase_add_test(tc_addnodes, AddVariableNode);
     tcase_add_test(tc_addnodes, AddVariableNode);
     tcase_add_test(tc_addnodes, AddVariableNode_Matrix);
     tcase_add_test(tc_addnodes, AddVariableNode_Matrix);
+    tcase_add_test(tc_addnodes, AddVariableNode_ExtensionObject);
     tcase_add_test(tc_addnodes, InstantiateVariableTypeNode);
     tcase_add_test(tc_addnodes, InstantiateVariableTypeNode);
     tcase_add_test(tc_addnodes, InstantiateVariableTypeNodeWrongDims);
     tcase_add_test(tc_addnodes, InstantiateVariableTypeNodeWrongDims);
     tcase_add_test(tc_addnodes, InstantiateVariableTypeNodeLessDims);
     tcase_add_test(tc_addnodes, InstantiateVariableTypeNodeLessDims);

+ 33 - 0
tests/server/check_services_view.c

@@ -129,6 +129,38 @@ START_TEST(Service_Browse_WithBrowseName) {
 }
 }
 END_TEST
 END_TEST
 
 
+START_TEST(Service_Browse_Recursive) {
+    UA_Server *server = UA_Server_new();
+    UA_ServerConfig_setDefault(UA_Server_getConfig(server));
+
+    size_t resultSize = 0;
+    UA_ExpandedNodeId *result = NULL;
+
+    UA_BrowseDescription bd;
+    UA_BrowseDescription_init(&bd);
+    bd.nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_NAMESPACEARRAY);
+    bd.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HIERARCHICALREFERENCES);
+    bd.includeSubtypes = true;
+    bd.browseDirection = UA_BROWSEDIRECTION_INVERSE;
+    UA_StatusCode retval = UA_Server_browseRecursive(server, &bd, &resultSize, &result);
+
+    ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
+    ck_assert_uint_eq(resultSize, 3);
+
+    UA_NodeId expected[3];
+    expected[0] = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER);
+    expected[1] = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
+    expected[2] = UA_NODEID_NUMERIC(0, UA_NS0ID_ROOTFOLDER);
+
+    for(size_t i = 0; i < resultSize; i++) {
+        ck_assert(UA_NodeId_equal(&expected[i], &result[i].nodeId));
+    }
+
+    UA_Array_delete(result, resultSize, &UA_TYPES[UA_TYPES_EXPANDEDNODEID]);
+    UA_Server_delete(server);
+}
+END_TEST
+
 START_TEST(Service_TranslateBrowsePathsToNodeIds) {
 START_TEST(Service_TranslateBrowsePathsToNodeIds) {
     UA_Client *client = UA_Client_new();
     UA_Client *client = UA_Client_new();
     UA_ClientConfig_setDefault(UA_Client_getConfig(client));
     UA_ClientConfig_setDefault(UA_Client_getConfig(client));
@@ -196,6 +228,7 @@ static Suite *testSuite_Service_TranslateBrowsePathsToNodeIds(void) {
     TCase *tc_browse = tcase_create("Browse Service");
     TCase *tc_browse = tcase_create("Browse Service");
     tcase_add_test(tc_browse, Service_Browse_WithBrowseName);
     tcase_add_test(tc_browse, Service_Browse_WithBrowseName);
     tcase_add_test(tc_browse, Service_Browse_WithMaxResults);
     tcase_add_test(tc_browse, Service_Browse_WithMaxResults);
+    tcase_add_test(tc_browse, Service_Browse_Recursive);
     suite_add_tcase(s, tc_browse);
     suite_add_tcase(s, tc_browse);
 
 
     TCase *tc_translate = tcase_create("TranslateBrowsePathsToNodeIds");
     TCase *tc_translate = tcase_create("TranslateBrowsePathsToNodeIds");

+ 86 - 0
tools/cmake/SetGitBasedVersion.cmake

@@ -0,0 +1,86 @@
+get_filename_component(VERSION_SRC_DIR ${CMAKE_CURRENT_LIST_DIR} DIRECTORY)
+set(VERSION_SRC_DIR "${VERSION_SRC_DIR}/..")
+
+find_package(Git)
+
+function(set_open62541_version)
+
+    # Generate a git-describe version string from Git repository tags
+    if(GIT_EXECUTABLE AND NOT DEFINED OPEN62541_VERSION)
+        execute_process(
+            COMMAND ${GIT_EXECUTABLE} describe --tags --dirty --match "v*"
+            WORKING_DIRECTORY "${VERSION_SRC_DIR}"
+            OUTPUT_VARIABLE GIT_DESCRIBE_VERSION
+            RESULT_VARIABLE GIT_DESCRIBE_ERROR_CODE
+            OUTPUT_STRIP_TRAILING_WHITESPACE
+        )
+        if(NOT GIT_DESCRIBE_ERROR_CODE)
+            set(OPEN62541_VERSION ${GIT_DESCRIBE_VERSION})
+
+            # Example values can be:
+            # v1.2
+            # v1.2.3
+            # v1.2.3-rc1
+            # v1.2.3-rc1-dirty
+            # v1.2.3-5-g4538abcd
+            # v1.2.3-5-g4538abcd-dirty
+
+            STRING(REGEX REPLACE "^(v[0-9\\.]+)(.*)$"
+                   "\\1"
+                   GIT_VERSION_NUMBERS
+                   "${GIT_DESCRIBE_VERSION}" )
+            STRING(REGEX REPLACE "^v([0-9\\.]+)(.*)$"
+                   "\\2"
+                   GIT_VERSION_LABEL
+                   "${GIT_DESCRIBE_VERSION}" )
+
+            if("${GIT_VERSION_NUMBERS}" MATCHES "^v([0-9]+)(.*)$")
+                STRING(REGEX REPLACE "^v([0-9]+)\\.?(.*)$"
+                       "\\1"
+                       GIT_VER_MAJOR
+                       "${GIT_VERSION_NUMBERS}" )
+                if("${GIT_VERSION_NUMBERS}" MATCHES "^v([0-9]+)\\.([0-9]+)(.*)$")
+                    STRING(REGEX REPLACE "^v([0-9]+)\\.([0-9]+)(.*)$"
+                           "\\2"
+                           GIT_VER_MINOR
+                           "${GIT_VERSION_NUMBERS}" )
+                    if("${GIT_VERSION_NUMBERS}" MATCHES "^v([0-9]+)\\.([0-9]+)\\.([0-9]+)(.*)$")
+                        STRING(REGEX REPLACE "^v([0-9]+)\\.([0-9]+)\\.([0-9]+)(.*)$"
+                               "\\3"
+                               GIT_VER_PATCH
+                               "${GIT_VERSION_NUMBERS}" )
+                    else()
+                        set(GIT_VER_PATCH 0)
+                    endif()
+                else()
+                    set(GIT_VER_MINOR 0)
+                    set(GIT_VER_PATCH 0)
+                endif()
+
+            else()
+                set(GIT_VER_MAJOR 0)
+                set(GIT_VER_MINOR 0)
+                set(GIT_VER_PATCH 0)
+            endif()
+            set(OPEN62541_VER_MAJOR ${GIT_VER_MAJOR} PARENT_SCOPE)
+            set(OPEN62541_VER_MINOR ${GIT_VER_MINOR} PARENT_SCOPE)
+            set(OPEN62541_VER_PATCH ${GIT_VER_PATCH} PARENT_SCOPE)
+            set(OPEN62541_VER_LABEL "${GIT_VERSION_LABEL}" PARENT_SCOPE)
+        endif()
+        set(OPEN62541_VER_COMMIT ${GIT_DESCRIBE_VERSION} PARENT_SCOPE)
+
+
+    endif()
+
+    # Final fallback: Just use a bogus version string that is semantically older
+    # than anything else and spit out a warning to the developer.
+    if(NOT DEFINED OPEN62541_VERSION)
+        set(OPEN62541_VERSION "v0.0.0-unknown")
+        set(OPEN62541_VER_MAJOR 0 PARENT_SCOPE)
+        set(OPEN62541_VER_MINOR 0 PARENT_SCOPE)
+        set(OPEN62541_VER_PATCH 0 PARENT_SCOPE)
+        set(OPEN62541_VER_LABEL "-unknown" PARENT_SCOPE)
+        set(OPEN62541_VER_COMMIT "undefined" PARENT_SCOPE)
+        message(WARNING "Failed to determine OPEN62541_VERSION from repository tags. Using default version \"${OPEN62541_VERSION}\".")
+    endif()
+endfunction()

+ 5 - 0
tools/travis/travis_linux_script.sh

@@ -438,6 +438,11 @@ echo -en 'travis_fold:end:script.build.unit_test_ns0_full\\r'
 
 
 if ! [ -z ${DEBIAN+x} ]; then
 if ! [ -z ${DEBIAN+x} ]; then
     echo -e "\r\n== Building the Debian package =="  && echo -en 'travis_fold:start:script.build.debian\\r'
     echo -e "\r\n== Building the Debian package =="  && echo -en 'travis_fold:start:script.build.debian\\r'
+    /usr/bin/$PYTHON ./debian/update_changelog.py
+    echo -e "\r\n --- New debian changelog content ---"
+    echo -e "--------------------------------------"
+    cat ./debian/changelog
+    echo -e "--------------------------------------"
     dpkg-buildpackage -b
     dpkg-buildpackage -b
     if [ $? -ne 0 ] ; then exit 1 ; fi
     if [ $? -ne 0 ] ; then exit 1 ; fi
     cp ../open62541*.deb .
     cp ../open62541*.deb .