Ver código fonte

Client: Store SecurityPolicies in the client config; Refactor connection logic

Julius Pfrommer 5 anos atrás
pai
commit
bcaa6695c0

+ 1 - 0
examples/CMakeLists.txt

@@ -122,6 +122,7 @@ endif()
 if(UA_ENABLE_ENCRYPTION)
     add_example(server_basic128rsa15 encryption/server_basic128rsa15.c)
     add_example(server_basic256sha256 encryption/server_basic256sha256.c)
+    add_example(client_encryption encryption/client_encryption.c)
 endif()
 
 add_example(custom_datatype_client custom_datatype/client_types_custom.c)

+ 36 - 33
examples/discovery/server_multicast.c

@@ -1,5 +1,6 @@
 /* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
  * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
+
 /*
  * A simple server instance which registers with the discovery server.
  * Compared to server_register.c this example waits until the LDS server announces
@@ -15,11 +16,11 @@
 
 #include <signal.h>
 
-UA_Boolean running = true;
-
 const UA_ByteString UA_SECURITY_POLICY_BASIC128_URI =
     {56, (UA_Byte *)"http://opcfoundation.org/UA/SecurityPolicy#Basic128Rsa15"};
 
+UA_Boolean running = true;
+
 static void stopHandler(int sign) {
     UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "received ctrl-c");
     running = false;
@@ -102,12 +103,13 @@ serverOnNetworkCallback(const UA_ServerOnNetwork *serverOnNetwork, UA_Boolean is
  */
 static
 UA_EndpointDescription *getRegisterEndpointFromServer(const char *discoveryServerUrl) {
-    UA_Client *client = UA_Client_new(UA_ClientConfig_default);
+    UA_Client *client = UA_Client_new();
+    UA_ClientConfig_setDefault(UA_Client_getConfig(client));
     UA_EndpointDescription *endpointArray = NULL;
     size_t endpointArraySize = 0;
     UA_StatusCode retval = UA_Client_getEndpoints(client, discoveryServerUrl,
                                                   &endpointArraySize, &endpointArray);
-    if (retval != UA_STATUSCODE_GOOD) {
+    if(retval != UA_STATUSCODE_GOOD) {
         UA_Array_delete(endpointArray, endpointArraySize,
                         &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]);
         UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER,
@@ -118,7 +120,7 @@ UA_EndpointDescription *getRegisterEndpointFromServer(const char *discoveryServe
 
     UA_LOG_DEBUG(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Server has %lu endpoints", (unsigned long)endpointArraySize);
     UA_EndpointDescription *foundEndpoint = NULL;
-    for (size_t i = 0; i < endpointArraySize; i++) {
+    for(size_t i = 0; i < endpointArraySize; i++) {
         UA_LOG_DEBUG(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "\tURL = %.*s, SecurityMode = %s",
                      (int) endpointArray[i].endpointUrl.length,
                      endpointArray[i].endpointUrl.data,
@@ -128,13 +130,13 @@ UA_EndpointDescription *getRegisterEndpointFromServer(const char *discoveryServe
                      "Invalid"
         );
         // find the endpoint with highest supported security mode
-        if ((UA_String_equal(&endpointArray[i].securityPolicyUri, &UA_SECURITY_POLICY_NONE_URI) ||
+        if((UA_String_equal(&endpointArray[i].securityPolicyUri, &UA_SECURITY_POLICY_NONE_URI) ||
             UA_String_equal(&endpointArray[i].securityPolicyUri, &UA_SECURITY_POLICY_BASIC128_URI)) && (
             foundEndpoint == NULL || foundEndpoint->securityMode < endpointArray[i].securityMode))
             foundEndpoint = &endpointArray[i];
     }
     UA_EndpointDescription *returnEndpoint = NULL;
-    if (foundEndpoint != NULL) {
+    if(foundEndpoint != NULL) {
         returnEndpoint = UA_EndpointDescription_new();
         UA_EndpointDescription_copy(foundEndpoint, returnEndpoint);
     }
@@ -151,12 +153,12 @@ UA_EndpointDescription *getRegisterEndpointFromServer(const char *discoveryServe
  * @return Returns the file content after parsing */
 static UA_ByteString loadFile(const char *const path) {
     UA_ByteString fileContents = UA_BYTESTRING_NULL;
-    if (path == NULL)
+    if(path == NULL)
         return fileContents;
 
     /* Open the file */
     FILE *fp = fopen(path, "rb");
-    if (!fp) {
+    if(!fp) {
         errno = 0; /* We read errno also from the tcp layer */
         return fileContents;
     }
@@ -165,10 +167,10 @@ static UA_ByteString loadFile(const char *const path) {
     fseek(fp, 0, SEEK_END);
     fileContents.length = (size_t) ftell(fp);
     fileContents.data = (UA_Byte *) UA_malloc(fileContents.length * sizeof(UA_Byte));
-    if (fileContents.data) {
+    if(fileContents.data) {
         fseek(fp, 0, SEEK_SET);
         size_t read = fread(fileContents.data, sizeof(UA_Byte), fileContents.length, fp);
-        if (read != fileContents.length)
+        if(read != fileContents.length)
             UA_ByteString_clear(&fileContents);
     } else {
         fileContents.length = 0;
@@ -192,18 +194,19 @@ static UA_ByteString loadFile(const char *const path) {
  */
 static
 UA_Client *getRegisterClient(UA_EndpointDescription *endpointRegister, int argc, char **argv) {
-    if (endpointRegister->securityMode == UA_MESSAGESECURITYMODE_NONE) {
+    if(endpointRegister->securityMode == UA_MESSAGESECURITYMODE_NONE) {
         UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Using LDS endpoint with security None");
-        return UA_Client_new(UA_ClientConfig_default);
+        UA_Client *client = UA_Client_new();
+        UA_ClientConfig_setDefault(UA_Client_getConfig(client));
+        return client;
     }
 #ifdef UA_ENABLE_ENCRYPTION
-    if (endpointRegister->securityMode == UA_MESSAGESECURITYMODE_SIGN) {
+    if(endpointRegister->securityMode == UA_MESSAGESECURITYMODE_SIGN) {
         UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER,
                     "LDS endpoint which only supports Sign is currently not supported");
         return NULL;
     }
 
-    UA_Client *clientRegister;
     UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER,
                 "Using LDS endpoint with security SignAndEncrypt");
 
@@ -215,7 +218,7 @@ UA_Client *getRegisterClient(UA_EndpointDescription *endpointRegister, int argc,
     size_t revocationListSize = 0;
 
 
-    if (argc < 3) {
+    if(argc < 3) {
         UA_LOG_FATAL(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
                      "The Certificate and key is missing."
                          "The required arguments are "
@@ -227,33 +230,32 @@ UA_Client *getRegisterClient(UA_EndpointDescription *endpointRegister, int argc,
     privateKey = loadFile(argv[2]);
 
     /* Load the trustList. Load revocationList is not supported now */
-    if (argc > 3) {
+    if(argc > 3) {
         trustListSize = (size_t) argc - 3;
         UA_StatusCode retval = UA_ByteString_allocBuffer(trustList, trustListSize);
-        if (retval != UA_STATUSCODE_GOOD) {
+        if(retval != UA_STATUSCODE_GOOD) {
             UA_ByteString_clear(&certificate);
             UA_ByteString_clear(&privateKey);
             return NULL;
         }
 
-        for (size_t trustListCount = 0; trustListCount < trustListSize; trustListCount++) {
+        for(size_t trustListCount = 0; trustListCount < trustListSize; trustListCount++) {
             trustList[trustListCount] = loadFile(argv[trustListCount + 3]);
         }
     }
 
-
     /* Secure client initialization */
-    clientRegister = UA_Client_secure_new(UA_ClientConfig_default,
-                                          certificate, privateKey,
-                                          &endpointRegister->serverCertificate,
-                                          trustList, trustListSize,
-                                          revocationList, revocationListSize,
-                                          UA_SecurityPolicy_Basic128Rsa15);
+    UA_Client *clientRegister = UA_Client_new();
+    UA_ClientConfig *cc = UA_Client_getConfig(clientRegister);
+    UA_ClientConfig_setDefaultEncryption(cc, certificate, privateKey,
+                                         trustList, trustListSize,
+                                         revocationList, revocationListSize);
+    cc->securityMode = UA_MESSAGESECURITYMODE_SIGNANDENCRYPT;
+
     UA_ByteString_clear(&certificate);
     UA_ByteString_clear(&privateKey);
-    for (size_t deleteCount = 0; deleteCount < trustListSize; deleteCount++) {
+    for(size_t deleteCount = 0; deleteCount < trustListSize; deleteCount++)
         UA_ByteString_clear(&trustList[deleteCount]);
-    }
 
     return clientRegister;
 #else
@@ -321,11 +323,12 @@ int main(int argc, char **argv) {
     }
     UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "LDS-ME server found on %s", discovery_url);
 
-    /* Check if the server supports sign and encrypt. OPC Foundation LDS requires an encrypted session for
-     * RegisterServer call, our server currently uses encrpytion optionally */
+    /* Check if the server supports sign and encrypt. OPC Foundation LDS
+     * requires an encrypted session for RegisterServer call, our server
+     * currently uses encrpytion optionally */
     UA_EndpointDescription *endpointRegister = getRegisterEndpointFromServer(discovery_url);
     UA_free(discovery_url);
-    if (endpointRegister == NULL || endpointRegister->securityMode == UA_MESSAGESECURITYMODE_INVALID) {
+    if(endpointRegister == NULL || endpointRegister->securityMode == UA_MESSAGESECURITYMODE_INVALID) {
         UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER,
                      "Could not find any suitable endpoints on discovery server");
         UA_Server_delete(server);
@@ -334,7 +337,7 @@ int main(int argc, char **argv) {
     }
 
     UA_Client *clientRegister = getRegisterClient(endpointRegister, argc, argv);
-    if (!clientRegister) {
+    if(!clientRegister) {
         UA_LOG_FATAL(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
                      "Could not create the client for remote registering");
         UA_Server_delete(server);
@@ -367,7 +370,7 @@ int main(int argc, char **argv) {
 
     // UNregister the server from the discovery server.
     retval = UA_Server_unregister_discovery(server, clientRegister);
-    if (retval != UA_STATUSCODE_GOOD)
+    if(retval != UA_STATUSCODE_GOOD)
         UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER,
                      "Could not unregister server from discovery server. "
                      "StatusCode %s", UA_StatusCode_name(retval));

+ 89 - 0
examples/encryption/client_encryption.c

@@ -0,0 +1,89 @@
+/* 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_server.h>
+#include <ua_config_default.h>
+#include <ua_log_stdout.h>
+#include <ua_securitypolicies.h>
+#include <ua_client_highlevel.h>
+#include "common.h"
+
+#include <signal.h>
+
+#define MIN_ARGS           3
+#define FAILURE            1
+#define CONNECTION_STRING  "opc.tcp://192.168.56.1:51510"
+
+/* main function for secure client implementation.
+ *
+ * @param  argc               count of command line variable provided
+ * @param  argv[]             array of strings include certificate, private key,
+ *                            trust list and revocation list
+ * @return Return an integer representing success or failure of application */
+int main(int argc, char* argv[]) {
+    UA_Client*              client             = NULL;
+    UA_StatusCode           retval             = UA_STATUSCODE_GOOD;
+    UA_ByteString*          revocationList     = NULL;
+    size_t                  revocationListSize = 0;
+    size_t                  trustListSize      = 0;
+    if(argc > MIN_ARGS)
+        trustListSize = (size_t)argc-MIN_ARGS;
+    UA_STACKARRAY(UA_ByteString, trustList, trustListSize);
+
+    if(argc < MIN_ARGS) {
+        UA_LOG_FATAL(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
+                     "The Certificate and key is missing."
+                     "The required arguments are "
+                     "<client-certificate.der> <client-private-key.der> "
+                     "[<trustlist1.crl>, ...]");
+        return FAILURE;
+    }
+
+    /* Load certificate and private key */
+    UA_ByteString           certificate        = loadFile(argv[1]);
+    UA_ByteString           privateKey         = loadFile(argv[2]);
+
+    /* Load the trustList. Load revocationList is not supported now */
+    for(size_t trustListCount = 0; trustListCount < trustListSize; trustListCount++)
+        trustList[trustListCount] = loadFile(argv[trustListCount+3]);
+
+    client = UA_Client_new();
+    UA_ClientConfig_setDefaultEncryption(UA_Client_getConfig(client),
+                                         certificate, privateKey,
+                                         trustList, trustListSize,
+                                         revocationList, revocationListSize);
+
+    UA_ByteString_clear(&certificate);
+    UA_ByteString_clear(&privateKey);
+    for(size_t deleteCount = 0; deleteCount < trustListSize; deleteCount++) {
+        UA_ByteString_clear(&trustList[deleteCount]);
+    }
+
+    /* Secure client connect */
+    //retval = UA_Client_connect(client, CONNECTION_STRING);
+    retval = UA_Client_connect_username(client, CONNECTION_STRING, "usr", "pwd");
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_Client_delete(client);
+        return (int)retval;
+    }
+
+    UA_Variant value;
+    UA_Variant_init(&value);
+
+    /* NodeId of the variable holding the current time */
+    const UA_NodeId nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME);
+    retval = UA_Client_readValueAttribute(client, nodeId, &value);
+
+    if(retval == UA_STATUSCODE_GOOD &&
+       UA_Variant_hasScalarType(&value, &UA_TYPES[UA_TYPES_DATETIME])) {
+        UA_DateTime raw_date  = *(UA_DateTime *) value.data;
+        UA_DateTimeStruct dts = UA_DateTime_toStruct(raw_date);
+        UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "date is: %u-%u-%u %u:%u:%u.%03u\n",
+                    dts.day, dts.month, dts.year, dts.hour, dts.min, dts.sec, dts.milliSec);
+    }
+
+    /* Clean up */
+    UA_Variant_clear(&value);
+    UA_Client_delete(client);
+    return (int)retval;
+}

+ 3 - 3
include/ua_client.h

@@ -96,9 +96,9 @@ UA_StatusCode UA_EXPORT
 UA_Client_connect(UA_Client *client, const char *endpointUrl);
 
 UA_StatusCode UA_EXPORT
-UA_Client_connect_async (UA_Client *client, const char *endpointUrl,
-                         UA_ClientAsyncServiceCallback callback,
-                         void *connected);
+UA_Client_connect_async(UA_Client *client, const char *endpointUrl,
+                        UA_ClientAsyncServiceCallback callback,
+                        void *connected);
 
 /* Connect to the server without creating a session
  *

+ 49 - 15
include/ua_client_config.h

@@ -49,31 +49,67 @@ typedef enum {
 } UA_ClientState;
 
 typedef struct {
-    UA_UInt32 timeout;               /* ASync + Sync response timeout in ms */
+    /* Basic client configuration */
+    void *clientContext; /* User-defined data attached to the client */
+    UA_Logger logger;   /* Logger used by the client */
+    UA_UInt32 timeout;  /* Response timeout in ms */
+    UA_ApplicationDescription clientDescription;
+
+    /* Basic connection configuration */
+    UA_ExtensionObject userIdentityToken; /* Configured User-Identity Token */
+    UA_MessageSecurityMode securityMode;  /* None, Sign, SignAndEncrypt. The
+                                           * default is invalid. This indicates
+                                           * the client to select any matching
+                                           * endpoint. */
+    UA_String securityPolicyUri; /* SecurityPolicy for the SecureChannel. An
+                                  * empty string indicates the client to select
+                                  * any matching SecurityPolicy. */
+
+    /* Advanced connection configuration
+     *
+     * If either endpoint or userTokenPolicy has been set (at least one non-zero
+     * byte in either structure), then the selected Endpoint and UserTokenPolicy
+     * overwrite the settings in the basic connection configuration. The
+     * userTokenPolicy array in the EndpointDescription is ignored. The selected
+     * userTokenPolicy is set in the dedicated configuration field.
+     *
+     * If the advanced configuration is not set, the client will write to it the
+     * selected Endpoint and UserTokenPolicy during GetEndpoints.
+     *
+     * The information in the advanced configuration is used during reconnect
+     * when the SecureChannel was broken. */
+    UA_EndpointDescription endpoint;
+    UA_UserTokenPolicy userTokenPolicy;
+
+    /* Advanced client configuration */
+
     UA_UInt32 secureChannelLifeTime; /* Lifetime in ms (then the channel needs
                                         to be renewed) */
-    UA_Logger logger;
+    UA_UInt32 requestedSessionTimeout; /* Session timeout in ms */
     UA_ConnectionConfig localConnectionConfig;
+    UA_UInt32 connectivityCheckInterval;     /* Connectivity check interval in ms.
+                                              * 0 = background task disabled */
+    const UA_DataTypeArray *customDataTypes; /* Custom DataTypes. Attention!
+                                              * Custom datatypes are not cleaned
+                                              * up together with the
+                                              * configuration. So it is possible
+                                              * to allocate them on ROM. */
+
+    /* Available SecurityPolicies */
+    size_t securityPoliciesSize;
+    UA_SecurityPolicy *securityPolicies;
+
+    /* Certificate Verification Plugin */
+    UA_CertificateVerification certificateVerification;
 
     /* Callbacks for async connection handshakes */
     UA_ConnectClientConnection connectionFunc;
     UA_ConnectClientConnection initConnectionFunc;
     void (*pollConnectionFunc)(UA_Client *client, void *context);
 
-    /* Custom DataTypes. Attention! Custom datatypes are not cleaned up together
-     * with the configuration. So it is possible to allocate them on ROM. */
-    const UA_DataTypeArray *customDataTypes;
-
     /* Callback for state changes */
     void (*stateCallback)(UA_Client *client, UA_ClientState clientState);
 
-   /* Connectivity check interval in ms.
-    * 0 = background task disabled */
-    UA_UInt32 connectivityCheckInterval;
-
-    /* session timeout in ms */
-    UA_UInt32 requestedSessionTimeout;
-
     /* When connectivityCheckInterval is greater than 0, every
      * connectivityCheckInterval (in ms), a async read request is performed on
      * the server. inactivityCallback is called when the client receive no
@@ -81,8 +117,6 @@ typedef struct {
      * attempt to recreate a healthy connection. */
     void (*inactivityCallback)(UA_Client *client);
 
-    void *clientContext;
-
 #ifdef UA_ENABLE_SUBSCRIPTIONS
     /* Number of PublishResponse queued up in the server */
     UA_UInt16 outStandingPublishRequests;

+ 0 - 7
include/ua_plugin_securitypolicy.h

@@ -390,13 +390,6 @@ UA_SecurityPolicy *
 UA_SecurityPolicy_getSecurityPolicyByUri(const UA_Server *server,
                                          UA_ByteString *securityPolicyUri);
 
-typedef UA_StatusCode
-(*UA_SecurityPolicy_Func)(UA_SecurityPolicy *policy,
-                          UA_CertificateVerification *certificateVerification,
-                          const UA_ByteString localCertificate,
-                          const UA_ByteString localPrivateKey,
-                          const UA_Logger *logger);
-
 _UA_END_DECLS
 
 #endif /* UA_PLUGIN_SECURITYPOLICY_H_ */

+ 60 - 19
plugins/ua_config_default.c

@@ -760,25 +760,25 @@ UA_ClientConfig_setDefault(UA_ClientConfig *config) {
 
     /* Certificate Verification that accepts every certificate. Can be
      * overwritten when the policy is specialized. */
-    //UA_CertificateVerification_AcceptAll(&config->certificateVerification);
-
-    /* if(config->securityPoliciesSize > 0) { */
-    /*     UA_LOG_ERROR(&config->logger, UA_LOGCATEGORY_NETWORK, */
-    /*                  "Could not initialize a config that already has SecurityPolicies"); */
-    /*     return UA_STATUSCODE_BADINTERNALERROR; */
-    /* } */
-
-    /* config->securityPolicies = (UA_SecurityPolicy*)malloc(sizeof(UA_SecurityPolicy)); */
-    /* if(!config->securityPolicies) */
-    /*     return UA_STATUSCODE_BADOUTOFMEMORY; */
-    /* UA_StatusCode retval = UA_SecurityPolicy_None(config->securityPolicies, NULL, */
-    /*                                               UA_BYTESTRING_NULL, &config->logger); */
-    /* if(retval != UA_STATUSCODE_GOOD) { */
-    /*     free(config->securityPolicies); */
-    /*     config->securityPolicies = NULL; */
-    /*     return retval; */
-    /* } */
-    /* config->securityPoliciesSize = 1; */
+    UA_CertificateVerification_AcceptAll(&config->certificateVerification);
+
+    if(config->securityPoliciesSize > 0) {
+        UA_LOG_ERROR(&config->logger, UA_LOGCATEGORY_NETWORK,
+                     "Could not initialize a config that already has SecurityPolicies");
+        return UA_STATUSCODE_BADINTERNALERROR;
+    }
+
+    config->securityPolicies = (UA_SecurityPolicy*)malloc(sizeof(UA_SecurityPolicy));
+    if(!config->securityPolicies)
+        return UA_STATUSCODE_BADOUTOFMEMORY;
+    UA_StatusCode retval = UA_SecurityPolicy_None(config->securityPolicies, NULL,
+                                                  UA_BYTESTRING_NULL, &config->logger);
+    if(retval != UA_STATUSCODE_GOOD) {
+        free(config->securityPolicies);
+        config->securityPolicies = NULL;
+        return retval;
+    }
+    config->securityPoliciesSize = 1;
 
     config->connectionFunc = UA_ClientConnectionTCP;
     config->initConnectionFunc = UA_ClientConnectionTCP_init; /* for async client */
@@ -800,3 +800,44 @@ UA_ClientConfig_setDefault(UA_ClientConfig *config) {
 
     return UA_STATUSCODE_GOOD;
 }
+
+#ifdef UA_ENABLE_ENCRYPTION
+UA_StatusCode
+UA_ClientConfig_setDefaultEncryption(UA_ClientConfig *config,
+                                     UA_ByteString localCertificate, UA_ByteString privateKey,
+                                     const UA_ByteString *trustList, size_t trustListSize,
+                                     const UA_ByteString *revocationList, size_t revocationListSize) {
+    UA_StatusCode retval = UA_ClientConfig_setDefault(config);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
+
+    retval = UA_CertificateVerification_Trustlist(&config->certificateVerification,
+                                                  trustList, trustListSize,
+                                                  revocationList, revocationListSize);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
+
+    /* Populate SecurityPolicies */
+    UA_SecurityPolicy *sp = (UA_SecurityPolicy*)
+        realloc(config->securityPolicies, sizeof(UA_SecurityPolicy) * 3);
+    if(!sp)
+        return UA_STATUSCODE_BADOUTOFMEMORY;
+    config->securityPolicies = sp;
+
+    retval = UA_SecurityPolicy_Basic128Rsa15(&config->securityPolicies[1],
+                                             &config->certificateVerification,
+                                             localCertificate, privateKey, &config->logger);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
+    ++config->securityPoliciesSize;
+
+    retval = UA_SecurityPolicy_Basic256Sha256(&config->securityPolicies[2],
+                                              &config->certificateVerification,
+                                              localCertificate, privateKey, &config->logger);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
+    ++config->securityPoliciesSize;
+
+    return UA_STATUSCODE_GOOD;
+}
+#endif

+ 8 - 0
plugins/ua_config_default.h

@@ -115,6 +115,14 @@ UA_ServerConfig_delete(UA_ServerConfig *config);
 UA_StatusCode UA_EXPORT
 UA_ClientConfig_setDefault(UA_ClientConfig *config);
 
+#ifdef UA_ENABLE_ENCRYPTION
+UA_StatusCode UA_EXPORT
+UA_ClientConfig_setDefaultEncryption(UA_ClientConfig *config,
+                                     UA_ByteString localCertificate, UA_ByteString privateKey,
+                                     const UA_ByteString *trustList, size_t trustListSize,
+                                     const UA_ByteString *revocationList, size_t revocationListSize);
+#endif
+
 _UA_END_DECLS
 
 #endif /* UA_CONFIG_DEFAULT_H_ */

+ 25 - 19
src/client/ua_client.c

@@ -34,8 +34,6 @@
 static void
 UA_Client_init(UA_Client* client) {
     memset(client, 0, sizeof(UA_Client));
-    UA_SecurityPolicy_None(&client->securityPolicy, NULL, UA_BYTESTRING_NULL,
-                           &client->config.logger);
     UA_SecureChannel_init(&client->channel);
     if(client->config.stateCallback)
         client->config.stateCallback(client, client->state);
@@ -55,24 +53,39 @@ UA_Client_new() {
     return client;
 }
 
+static void
+UA_ClientConfig_deleteMembers(UA_ClientConfig *config) {
+    UA_ApplicationDescription_deleteMembers(&config->clientDescription);
+
+    UA_ExtensionObject_deleteMembers(&config->userIdentityToken);
+    UA_String_deleteMembers(&config->securityPolicyUri);
+
+    UA_EndpointDescription_deleteMembers(&config->endpoint);
+    UA_UserTokenPolicy_deleteMembers(&config->userTokenPolicy);
+
+    if(config->certificateVerification.deleteMembers)
+        config->certificateVerification.deleteMembers(&config->certificateVerification);
+
+    /* Delete the SecurityPolicies */
+    if(config->securityPolicies == 0)
+        return;
+    for(size_t i = 0; i < config->securityPoliciesSize; i++)
+        config->securityPolicies[i].deleteMembers(&config->securityPolicies[i]);
+    UA_free(config->securityPolicies);
+    config->securityPolicies = 0;
+}
+
 static void
 UA_Client_deleteMembers(UA_Client *client) {
     UA_Client_disconnect(client);
-    client->securityPolicy.deleteMembers(&client->securityPolicy);
     /* Commented as UA_SecureChannel_deleteMembers already done
      * in UA_Client_disconnect function */
     //UA_SecureChannel_deleteMembersCleanup(&client->channel);
     if (client->connection.free)
         client->connection.free(&client->connection);
     UA_Connection_deleteMembers(&client->connection);
-    if(client->endpointUrl.data)
-        UA_String_deleteMembers(&client->endpointUrl);
-    UA_UserTokenPolicy_deleteMembers(&client->token);
     UA_NodeId_deleteMembers(&client->authenticationToken);
-    if(client->username.data)
-        UA_String_deleteMembers(&client->username);
-    if(client->password.data)
-        UA_String_deleteMembers(&client->password);
+    UA_String_deleteMembers(&client->endpointUrl);
 
     /* Delete the async service calls */
     UA_Client_AsyncService_removeAll(client, UA_STATUSCODE_BADSHUTDOWN);
@@ -87,6 +100,8 @@ UA_Client_deleteMembers(UA_Client *client) {
 
     /* Clean up the work queue */
     UA_WorkQueue_cleanup(&client->workQueue);
+
+    UA_ClientConfig_deleteMembers(&client->config);
 }
 
 void
@@ -97,15 +112,6 @@ UA_Client_reset(UA_Client* client) {
 
 void
 UA_Client_delete(UA_Client* client) {
-    /* certificate verification is initialized for secure client
-     * which is deallocated */
-    if(client->channel.securityMode == UA_MESSAGESECURITYMODE_SIGN ||
-       client->channel.securityMode == UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) {
-        if (client->securityPolicy.certificateVerification->deleteMembers)
-            client->securityPolicy.certificateVerification->deleteMembers(client->securityPolicy.certificateVerification);
-        UA_free(client->securityPolicy.certificateVerification);
-    }
-
     UA_Client_deleteMembers(client);
     UA_free(client);
 }

+ 263 - 116
src/client/ua_client_connect.c

@@ -89,7 +89,7 @@ processACKResponse(void *application, UA_Connection *connection, UA_ByteString *
 }
 
 static UA_StatusCode
-HelAckHandshake(UA_Client *client) {
+HelAckHandshake(UA_Client *client, const UA_String endpointUrl) {
     /* Get a buffer */
     UA_ByteString message;
     UA_Connection *conn = &client->connection;
@@ -99,7 +99,7 @@ HelAckHandshake(UA_Client *client) {
 
     /* Prepare the HEL message and encode at offset 8 */
     UA_TcpHelloMessage hello;
-    UA_String_copy(&client->endpointUrl, &hello.endpointUrl); /* must be less than 4096 bytes */
+    UA_String_copy(&endpointUrl, &hello.endpointUrl); /* must be less than 4096 bytes */
     memcpy(&hello, &client->config.localConnectionConfig, sizeof(UA_ConnectionConfig)); /* same struct layout */
 
     UA_Byte *bufPos = &message.data[8]; /* skip the header */
@@ -147,6 +147,15 @@ HelAckHandshake(UA_Client *client) {
     return retval;
 }
 
+UA_SecurityPolicy *
+getSecurityPolicy(UA_Client *client, UA_String policyUri) {
+    for(size_t i = 0; i < client->config.securityPoliciesSize; i++) {
+        if(UA_String_equal(&policyUri, &client->config.securityPolicies[i].policyUri))
+            return &client->config.securityPolicies[i];
+    }
+    return NULL;
+}
+
 static void
 processDecodedOPNResponse(UA_Client *client, UA_OpenSecureChannelResponse *response, UA_Boolean renew) {
     /* Replace the token */
@@ -345,26 +354,28 @@ activateSession(UA_Client *client) {
     request.requestHeader.requestHandle = ++client->requestHandle;
     request.requestHeader.timestamp = UA_DateTime_now();
     request.requestHeader.timeoutHint = 600000;
+    UA_StatusCode retval =
+        UA_ExtensionObject_copy(&client->config.userIdentityToken, &request.userIdentityToken);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
 
-    //manual ExtensionObject encoding of the identityToken
-    if(client->authenticationMethod == UA_CLIENTAUTHENTICATION_NONE) {
-        UA_AnonymousIdentityToken* identityToken = UA_AnonymousIdentityToken_new();
-        UA_AnonymousIdentityToken_init(identityToken);
-        UA_String_copy(&client->token.policyId, &identityToken->policyId);
-        request.userIdentityToken.encoding = UA_EXTENSIONOBJECT_DECODED;
+    /* If not token is set, use anonymous */
+    if(request.userIdentityToken.encoding == UA_EXTENSIONOBJECT_ENCODED_NOBODY) {
+        UA_AnonymousIdentityToken *t = UA_AnonymousIdentityToken_new();
+        if(!t) {
+            UA_ActivateSessionRequest_deleteMembers(&request);
+            return UA_STATUSCODE_BADOUTOFMEMORY;
+        }
+        request.userIdentityToken.content.decoded.data = t;
         request.userIdentityToken.content.decoded.type = &UA_TYPES[UA_TYPES_ANONYMOUSIDENTITYTOKEN];
-        request.userIdentityToken.content.decoded.data = identityToken;
-    } else {
-        UA_UserNameIdentityToken* identityToken = UA_UserNameIdentityToken_new();
-        UA_UserNameIdentityToken_init(identityToken);
-        UA_String_copy(&client->token.policyId, &identityToken->policyId);
-        UA_String_copy(&client->username, &identityToken->userName);
-        UA_String_copy(&client->password, &identityToken->password);
         request.userIdentityToken.encoding = UA_EXTENSIONOBJECT_DECODED;
-        request.userIdentityToken.content.decoded.type = &UA_TYPES[UA_TYPES_USERNAMEIDENTITYTOKEN];
-        request.userIdentityToken.content.decoded.data = identityToken;
     }
 
+    /* Set the policy-Id from the endpoint. Every IdentityToken starts with a
+     * string. */
+    retval |= UA_String_copy(&client->config.userTokenPolicy.policyId,
+                             (UA_String*)request.userIdentityToken.content.decoded.data);
+
     /* This function call is to prepare a client signature */
     if(client->channel.securityMode == UA_MESSAGESECURITYMODE_SIGN ||
        client->channel.securityMode == UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) {
@@ -381,7 +392,7 @@ activateSession(UA_Client *client) {
                      UA_StatusCode_name(response.responseHeader.serviceResult));
     }
 
-    UA_StatusCode retval = response.responseHeader.serviceResult;
+    retval = response.responseHeader.serviceResult;
     UA_ActivateSessionRequest_deleteMembers(&request);
     UA_ActivateSessionResponse_deleteMembers(&response);
     return retval;
@@ -389,14 +400,15 @@ activateSession(UA_Client *client) {
 
 /* Gets a list of endpoints. Memory is allocated for endpointDescription array */
 UA_StatusCode
-UA_Client_getEndpointsInternal(UA_Client *client, size_t* endpointDescriptionsSize,
-                               UA_EndpointDescription** endpointDescriptions) {
+UA_Client_getEndpointsInternal(UA_Client *client, const UA_String endpointUrl,
+                               size_t *endpointDescriptionsSize,
+                               UA_EndpointDescription **endpointDescriptions) {
     UA_GetEndpointsRequest request;
     UA_GetEndpointsRequest_init(&request);
     request.requestHeader.timestamp = UA_DateTime_now();
     request.requestHeader.timeoutHint = 10000;
     // assume the endpointurl outlives the service call
-    request.endpointUrl = client->endpointUrl;
+    request.endpointUrl = endpointUrl;
 
     UA_GetEndpointsResponse response;
     __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_GETENDPOINTSREQUEST],
@@ -419,61 +431,126 @@ UA_Client_getEndpointsInternal(UA_Client *client, size_t* endpointDescriptionsSi
 }
 
 static UA_StatusCode
-getEndpoints(UA_Client *client) {
+selectEndpoint(UA_Client *client, const UA_String endpointUrl) {
     UA_EndpointDescription* endpointArray = NULL;
     size_t endpointArraySize = 0;
     UA_StatusCode retval =
-        UA_Client_getEndpointsInternal(client, &endpointArraySize, &endpointArray);
+        UA_Client_getEndpointsInternal(client, endpointUrl, &endpointArraySize, &endpointArray);
     if(retval != UA_STATUSCODE_GOOD)
         return retval;
 
     UA_Boolean endpointFound = false;
     UA_Boolean tokenFound = false;
-    UA_String securityNone = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#None");
     UA_String binaryTransport = UA_STRING("http://opcfoundation.org/UA-Profile/"
                                           "Transport/uatcp-uasc-uabinary");
 
-    // TODO: compare endpoint information with client->endpointUri
     for(size_t i = 0; i < endpointArraySize; ++i) {
         UA_EndpointDescription* endpoint = &endpointArray[i];
-        /* look out for binary transport endpoints */
-        /* Note: Siemens returns empty ProfileUrl, we will accept it as binary */
+        /* Match Binary TransportProfile?
+         * Note: Siemens returns empty ProfileUrl, we will accept it as binary */
         if(endpoint->transportProfileUri.length != 0 &&
            !UA_String_equal(&endpoint->transportProfileUri, &binaryTransport))
             continue;
 
-        /* look for an endpoint corresponding to the client security policy */
-        if(!UA_String_equal(&endpoint->securityPolicyUri, &client->securityPolicy.policyUri))
+        /* Valid SecurityMode? */
+        if(endpoint->securityMode < 1 || endpoint->securityMode > 3)
+            continue;
+
+        /* Selected SecurityMode? */
+        if(client->config.securityMode > 0 &&
+           client->config.securityMode != endpoint->securityMode)
+            continue;
+
+        /* Matching SecurityPolicy? */
+        if(client->config.securityPolicyUri.length > 0 &&
+           !UA_String_equal(&client->config.securityPolicyUri,
+                            &endpoint->securityPolicyUri))
+            continue;
+
+        /* SecurityPolicy available? */
+        if(!getSecurityPolicy(client, endpoint->securityPolicyUri))
             continue;
 
         endpointFound = true;
 
-        /* look for a user token policy with an anonymous token */
+        /* Select a matching UserTokenPolicy inside the endpoint */
         for(size_t j = 0; j < endpoint->userIdentityTokensSize; ++j) {
             UA_UserTokenPolicy* userToken = &endpoint->userIdentityTokens[j];
 
             /* Usertokens also have a security policy... */
             if(userToken->securityPolicyUri.length > 0 &&
-               !UA_String_equal(&userToken->securityPolicyUri, &securityNone))
+               !getSecurityPolicy(client, userToken->securityPolicyUri))
+                continue;
+
+            if(userToken->tokenType > 3)
                 continue;
 
-            /* UA_CLIENTAUTHENTICATION_NONE == UA_USERTOKENTYPE_ANONYMOUS
-             * UA_CLIENTAUTHENTICATION_USERNAME == UA_USERTOKENTYPE_USERNAME
-             * TODO: Check equivalence for other types when adding the support */
-            if((int)client->authenticationMethod != (int)userToken->tokenType)
+            /* Does the token type match the client configuration? */
+            if((userToken->tokenType == UA_USERTOKENTYPE_ANONYMOUS &&
+                client->config.userIdentityToken.content.decoded.type !=
+                &UA_TYPES[UA_TYPES_ANONYMOUSIDENTITYTOKEN] &&
+                client->config.userIdentityToken.content.decoded.type != NULL) ||
+               (userToken->tokenType == UA_USERTOKENTYPE_USERNAME &&
+                client->config.userIdentityToken.content.decoded.type !=
+                &UA_TYPES[UA_TYPES_USERNAMEIDENTITYTOKEN]) ||
+               (userToken->tokenType == UA_USERTOKENTYPE_CERTIFICATE &&
+                client->config.userIdentityToken.content.decoded.type !=
+                &UA_TYPES[UA_TYPES_X509IDENTITYTOKEN]) ||
+               (userToken->tokenType == UA_USERTOKENTYPE_ISSUEDTOKEN &&
+                client->config.userIdentityToken.content.decoded.type !=
+                &UA_TYPES[UA_TYPES_ISSUEDIDENTITYTOKEN]))
                 continue;
 
-            /* Endpoint with matching usertokenpolicy found */
+            /* Endpoint with matching UserTokenPolicy found. Copy to the
+             * configuration. */
             tokenFound = true;
-            UA_UserTokenPolicy_deleteMembers(&client->token);
-            UA_UserTokenPolicy_copy(userToken, &client->token);
+            UA_EndpointDescription_deleteMembers(&client->config.endpoint);
+            UA_EndpointDescription temp = *endpoint;
+            temp.userIdentityTokensSize = 0;
+            temp.userIdentityTokens = NULL;
+            retval = UA_EndpointDescription_copy(&temp, &client->config.endpoint);
+            UA_UserTokenPolicy_deleteMembers(&client->config.userTokenPolicy);
+            retval |= UA_UserTokenPolicy_copy(userToken, &client->config.userTokenPolicy);
+
+            if(retval != UA_STATUSCODE_GOOD)
+                break;
+
+#if UA_LOGLEVEL <= 300
+            const char *securityModeNames[3] = {"None", "Sign", "SignAndEncrypt"};
+            const char *userTokenTypeNames[4] = {"Anonymous", "UserName",
+                                                 "Certificate", "IssuedToken"};
+            UA_String *securityPolicyUri = &userToken->securityPolicyUri;
+            if(securityPolicyUri->length == 0)
+                securityPolicyUri = &endpoint->securityPolicyUri;
+#endif
+
+            /* Log the selected endpoint */
+            UA_LOG_INFO(&client->config.logger, UA_LOGCATEGORY_CLIENT,
+                        "Selected Endpoint %.*s with SecurityMode %s and SecurityPolicy %.*s",
+                        (int)endpoint->endpointUrl.length, endpoint->endpointUrl.data,
+                        securityModeNames[endpoint->securityMode - 1],
+                        (int)endpoint->securityPolicyUri.length,
+                        endpoint->securityPolicyUri.data);
+
+            /* Log the selected UserTokenPolicy */
+            UA_LOG_INFO(&client->config.logger, UA_LOGCATEGORY_CLIENT,
+                        "Selected UserTokenPolicy %.*s with UserTokenType %s and SecurityPolicy %.*s",
+                        (int)userToken->policyId.length, userToken->policyId.data,
+                        userTokenTypeNames[userToken->tokenType],
+                        (int)securityPolicyUri->length, securityPolicyUri->data);
             break;
         }
+
+        if(tokenFound)
+            break;
     }
 
     UA_Array_delete(endpointArray, endpointArraySize,
                     &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]);
 
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
+
     if(!endpointFound) {
         UA_LOG_ERROR(&client->config.logger, UA_LOGCATEGORY_CLIENT,
                      "No suitable endpoint found");
@@ -513,7 +590,10 @@ createSession(UA_Client *client) {
     UA_ByteString_copy(&client->channel.localNonce, &request.clientNonce);
     request.requestedSessionTimeout = client->config.requestedSessionTimeout;
     request.maxResponseMessageSize = UA_INT32_MAX;
-    UA_String_copy(&client->endpointUrl, &request.endpointUrl);
+    UA_String_copy(&client->config.endpoint.endpointUrl, &request.endpointUrl);
+
+    UA_ApplicationDescription_copy(&client->config.clientDescription,
+                                   &request.clientDescription);
 
     if(client->channel.securityMode == UA_MESSAGESECURITYMODE_SIGN ||
        client->channel.securityMode == UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) {
@@ -551,8 +631,7 @@ createSession(UA_Client *client) {
 }
 
 UA_StatusCode
-UA_Client_connectInternal(UA_Client *client, const char *endpointUrl,
-                          UA_Boolean endpointsHandshake, UA_Boolean createNewSession) {
+UA_Client_connectTCPSecureChannel(UA_Client *client, const UA_String endpointUrl) {
     if(client->state >= UA_CLIENTSTATE_CONNECTED)
         return UA_STATUSCODE_GOOD;
     UA_ChannelSecurityToken_init(&client->channel.securityToken);
@@ -560,113 +639,174 @@ UA_Client_connectInternal(UA_Client *client, const char *endpointUrl,
     client->channel.sendSequenceNumber = 0;
     client->requestId = 0;
 
-    UA_StatusCode retval = UA_STATUSCODE_GOOD;
-    client->connection =
-        client->config.connectionFunc(client->config.localConnectionConfig,
-                                      UA_STRING((char*)(uintptr_t)endpointUrl),
-                                      client->config.timeout, &client->config.logger);
-    if(client->connection.state != UA_CONNECTION_OPENING) {
-        retval = UA_STATUSCODE_BADCONNECTIONCLOSED;
-        goto cleanup;
-    }
-
-    UA_String_deleteMembers(&client->endpointUrl);
-    client->endpointUrl = UA_STRING_ALLOC(endpointUrl);
-    if(!client->endpointUrl.data) {
-        retval = UA_STATUSCODE_BADOUTOFMEMORY;
-        goto cleanup;
-    }
-
-    /* Set the channel SecurityMode if not done so far */
+    /* Set the channel SecurityMode */
+    client->channel.securityMode = client->config.endpoint.securityMode;
     if(client->channel.securityMode == UA_MESSAGESECURITYMODE_INVALID)
         client->channel.securityMode = UA_MESSAGESECURITYMODE_NONE;
 
-    /* Set the channel SecurityPolicy if not done so far */
+    /* Initialized the SecureChannel */
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+    UA_LOG_DEBUG(&client->config.logger, UA_LOGCATEGORY_CLIENT,
+                 "Initialize the SecurityPolicy context");
     if(!client->channel.securityPolicy) {
-        UA_ByteString remoteCertificate = UA_BYTESTRING_NULL;
-        retval = UA_SecureChannel_setSecurityPolicy(&client->channel,
-                                                    &client->securityPolicy,
-                                                    &remoteCertificate);
+        /* Set the channel SecurityPolicy to #None if no endpoint is selected */
+        UA_String sps = client->config.endpoint.securityPolicyUri;
+        if(client->config.endpoint.securityPolicyUri.length == 0)
+            sps = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#None");
+        UA_SecurityPolicy *sp = getSecurityPolicy(client, sps);
+        if(!sp) {
+            retval = UA_STATUSCODE_BADINTERNALERROR;
+            goto cleanup;
+        }
+        retval =
+            UA_SecureChannel_setSecurityPolicy(&client->channel, sp,
+                                               &client->config.endpoint.serverCertificate);
         if(retval != UA_STATUSCODE_GOOD)
-            return retval;
+            goto cleanup;
     }
 
-    /* Local nonce set and generate sym keys */
-    retval = UA_SecureChannel_generateLocalNonce(&client->channel);
-    if(retval != UA_STATUSCODE_GOOD)
-        return retval;
-
     /* Open a TCP connection */
+    client->connection = client->config.connectionFunc(client->config.localConnectionConfig,
+                                                       endpointUrl, client->config.timeout,
+                                                       &client->config.logger);
+    if(client->connection.state != UA_CONNECTION_OPENING) {
+        UA_LOG_ERROR(&client->config.logger, UA_LOGCATEGORY_CLIENT,
+                     "Opening the TCP socket failed");
+        retval = UA_STATUSCODE_BADCONNECTIONCLOSED;
+        goto cleanup;
+    }
+
+    UA_LOG_INFO(&client->config.logger, UA_LOGCATEGORY_CLIENT,
+                "TCP connection established");
+
+    /* Perform the HEL/ACK handshake */
     client->connection.config = client->config.localConnectionConfig;
-    retval = HelAckHandshake(client);
+    retval = HelAckHandshake(client, endpointUrl);
     if(retval != UA_STATUSCODE_GOOD)
         goto cleanup;
     setClientState(client, UA_CLIENTSTATE_CONNECTED);
 
-    /* Open a SecureChannel. TODO: Select with endpoint  */
+    /* Open a SecureChannel. */
+    retval = UA_SecureChannel_generateLocalNonce(&client->channel);
+    if(retval != UA_STATUSCODE_GOOD)
+        goto cleanup;
     client->channel.connection = &client->connection;
     retval = openSecureChannel(client, false);
     if(retval != UA_STATUSCODE_GOOD)
         goto cleanup;
+    retval = UA_SecureChannel_generateNewKeys(&client->channel);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
     setClientState(client, UA_CLIENTSTATE_SECURECHANNEL);
 
+    return retval;
+
+cleanup:
+    UA_Client_disconnect(client);
+    return retval;
+}
+
+UA_StatusCode
+UA_Client_connectSession(UA_Client *client) {
+    if(client->state < UA_CLIENTSTATE_SECURECHANNEL)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
     /* Delete async service. TODO: Move this from connect to the disconnect/cleanup phase */
     UA_Client_AsyncService_removeAll(client, UA_STATUSCODE_BADSHUTDOWN);
 
-#ifdef UA_ENABLE_SUBSCRIPTIONS
-    client->currentlyOutStandingPublishRequests = 0;
-#endif
-
-// TODO: actually, reactivate an existing session is working, but currently republish is not implemented
-// This option is disabled until we have a good implementation of the subscription recovery.
+    // TODO: actually, reactivate an existing session is working, but currently
+    // republish is not implemented This option is disabled until we have a good
+    // implementation of the subscription recovery.
 
 #ifdef UA_SESSION_RECOVERY
     /* Try to activate an existing Session for this SecureChannel */
     if((!UA_NodeId_equal(&client->authenticationToken, &UA_NODEID_NULL)) && (createNewSession)) {
-        retval = activateSession(client);
-        if(retval == UA_STATUSCODE_BADSESSIONIDINVALID) {
-            /* Could not recover an old session. Remove authenticationToken */
-            UA_NodeId_deleteMembers(&client->authenticationToken);
-        } else {
-            if(retval != UA_STATUSCODE_GOOD)
-                goto cleanup;
-            setClientState(client, UA_CLIENTSTATE_SESSION_RENEWED);
-            return retval;
+        UA_StatusCode res = activateSession(client);
+        if(res != UA_STATUSCODE_BADSESSIONIDINVALID) {
+            if(res == UA_STATUSCODE_GOOD)
+                setClientState(client, UA_CLIENTSTATE_SESSION_RENEWED);
+            else
+                UA_Client_disconnect(client);
+            return res;
         }
-    } else {
-        UA_NodeId_deleteMembers(&client->authenticationToken);
     }
-#else
-    UA_NodeId_deleteMembers(&client->authenticationToken);
 #endif /* UA_SESSION_RECOVERY */
 
-    /* Generate new local and remote key */
-    retval = UA_SecureChannel_generateNewKeys(&client->channel);
-    if(retval != UA_STATUSCODE_GOOD)
-        return retval;
+    /* Could not recover an old session. Remove authenticationToken */
+    UA_NodeId_deleteMembers(&client->authenticationToken);
 
-    /* Get Endpoints */
-    if(endpointsHandshake) {
-        retval = getEndpoints(client);
-        if(retval != UA_STATUSCODE_GOOD)
-            goto cleanup;
+    /* Create a session */
+    UA_LOG_DEBUG(&client->config.logger, UA_LOGCATEGORY_CLIENT, "Create a new session");
+    UA_StatusCode retval = createSession(client);
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_Client_disconnect(client);
+        return retval;
     }
-
-    /* Create the Session for this SecureChannel */
-    if(createNewSession) {
-        retval = createSession(client);
-        if(retval != UA_STATUSCODE_GOOD)
-            goto cleanup;
+    
+    /* A new session has been created. We need to clean up the subscriptions */
 #ifdef UA_ENABLE_SUBSCRIPTIONS
-        /* A new session has been created. We need to clean up the subscriptions */
-        UA_Client_Subscriptions_clean(client);
+    UA_Client_Subscriptions_clean(client);
+    client->currentlyOutStandingPublishRequests = 0;
 #endif
-        retval = activateSession(client);
+
+    /* Activate the session */
+    retval = activateSession(client);
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_Client_disconnect(client);
+        return retval;
+    }
+    setClientState(client, UA_CLIENTSTATE_SESSION);
+    return retval;
+}
+
+UA_StatusCode
+UA_Client_connectInternal(UA_Client *client, const UA_String endpointUrl) {
+    if(client->state >= UA_CLIENTSTATE_CONNECTED)
+        return UA_STATUSCODE_GOOD;
+
+    UA_LOG_INFO(&client->config.logger, UA_LOGCATEGORY_CLIENT,
+                "Connecting to endpoint %.*s", (int)endpointUrl.length,
+                endpointUrl.data);
+
+    /* Get endpoints only if the description has not been touched (memset to
+     * zero) */
+    UA_Byte test = 0;
+    UA_Byte *pos = (UA_Byte*)&client->config.endpoint;
+    for(size_t i = 0; i < sizeof(UA_EndpointDescription); i++)
+        test = test | pos[i];
+    pos = (UA_Byte*)&client->config.userTokenPolicy;
+    for(size_t i = 0; i < sizeof(UA_UserTokenPolicy); i++)
+        test = test | pos[i];
+    UA_Boolean getEndpoints = (test == 0);
+
+    /* Connect up to the SecureChannel */
+    UA_StatusCode retval = UA_Client_connectTCPSecureChannel(client, endpointUrl);
+    if(retval != UA_STATUSCODE_GOOD)
+        goto cleanup;
+
+    /* Get and select endpoints if required */
+    if(getEndpoints) {
+        UA_LOG_INFO(&client->config.logger, UA_LOGCATEGORY_CLIENT,
+                    "Endpoint and UserTokenPolicy unconfigured, perform GetEndpoints");
+        retval = selectEndpoint(client, endpointUrl);
         if(retval != UA_STATUSCODE_GOOD)
             goto cleanup;
-        setClientState(client, UA_CLIENTSTATE_SESSION);
+
+        /* Reconnect with a new SecureChannel if the current one does not match
+         * the selected endpoint */
+        if(!UA_String_equal(&client->config.endpoint.securityPolicyUri,
+                            &client->channel.securityPolicy->policyUri)) {
+            UA_LOG_INFO(&client->config.logger, UA_LOGCATEGORY_CLIENT,
+                        "Disconnect to switch to a different SecurityPolicy");
+            UA_Client_disconnect(client);
+            return UA_Client_connectInternal(client, endpointUrl);
+        }
     }
 
+    retval = UA_Client_connectSession(client);
+    if(retval != UA_STATUSCODE_GOOD)
+        goto cleanup;
+
     return retval;
 
 cleanup:
@@ -676,20 +816,26 @@ cleanup:
 
 UA_StatusCode
 UA_Client_connect(UA_Client *client, const char *endpointUrl) {
-    return UA_Client_connectInternal(client, endpointUrl, true, true);
+    return UA_Client_connectInternal(client, UA_STRING((char*)(uintptr_t)endpointUrl));
 }
 
 UA_StatusCode
 UA_Client_connect_noSession(UA_Client *client, const char *endpointUrl) {
-    return UA_Client_connectInternal(client, endpointUrl, true, false);
+    return UA_Client_connectTCPSecureChannel(client, UA_STRING((char*)(uintptr_t)endpointUrl));
 }
 
 UA_StatusCode
 UA_Client_connect_username(UA_Client *client, const char *endpointUrl,
                            const char *username, const char *password) {
-    client->authenticationMethod = UA_CLIENTAUTHENTICATION_USERNAME;
-    client->username = UA_STRING_ALLOC(username);
-    client->password = UA_STRING_ALLOC(password);
+    UA_UserNameIdentityToken* identityToken = UA_UserNameIdentityToken_new();
+    if(!identityToken)
+        return UA_STATUSCODE_BADOUTOFMEMORY;
+    identityToken->userName = UA_STRING_ALLOC(username);
+    identityToken->password = UA_STRING_ALLOC(password);
+    UA_ExtensionObject_deleteMembers(&client->config.userIdentityToken);
+    client->config.userIdentityToken.encoding = UA_EXTENSIONOBJECT_DECODED;
+    client->config.userIdentityToken.content.decoded.type = &UA_TYPES[UA_TYPES_USERNAMEIDENTITYTOKEN];
+    client->config.userIdentityToken.content.decoded.data = identityToken;
     return UA_Client_connect(client, endpointUrl);
 }
 
@@ -752,13 +898,14 @@ UA_Client_disconnect(UA_Client *client) {
         if(client->connection.close != NULL)
             client->connection.close(&client->connection);
 
-
 #ifdef UA_ENABLE_SUBSCRIPTIONS
 // TODO REMOVE WHEN UA_SESSION_RECOVERY IS READY
     /* We need to clean up the subscriptions */
     UA_Client_Subscriptions_clean(client);
 #endif
 
+    UA_SecureChannel_deleteMembers(&client->channel);
+
     setClientState(client, UA_CLIENTSTATE_DISCONNECTED);
     return UA_STATUSCODE_GOOD;
 }

+ 74 - 48
src/client/ua_client_connect_async.c

@@ -95,7 +95,8 @@ sendHELMessage(UA_Client *client) {
     /* Prepare the HEL message and encode at offset 8 */
     UA_TcpHelloMessage hello;
     UA_String_copy(&client->endpointUrl, &hello.endpointUrl); /* must be less than 4096 bytes */
-    memcpy(&hello, &client->config.localConnectionConfig, sizeof(UA_ConnectionConfig)); /* same struct layout */
+    memcpy(&hello, &client->config.localConnectionConfig,
+           sizeof(UA_ConnectionConfig)); /* same struct layout */
 
     UA_Byte *bufPos = &message.data[8]; /* skip the header */
     const UA_Byte *bufEnd = &message.data[message.length];
@@ -332,39 +333,50 @@ requestActivateSession (UA_Client *client, UA_UInt32 *requestId) {
     request.requestHeader.requestHandle = ++client->requestHandle;
     request.requestHeader.timestamp = UA_DateTime_now ();
     request.requestHeader.timeoutHint = 600000;
+    UA_StatusCode retval =
+        UA_ExtensionObject_copy(&client->config.userIdentityToken, &request.userIdentityToken);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
 
-    /* Manual ExtensionObject encoding of the identityToken */
-    if (client->authenticationMethod == UA_CLIENTAUTHENTICATION_NONE) {
-        UA_AnonymousIdentityToken* identityToken =
-                UA_AnonymousIdentityToken_new();
-        UA_AnonymousIdentityToken_init (identityToken);
-        UA_String_copy(&client->token.policyId, &identityToken->policyId);
-        request.userIdentityToken.encoding = UA_EXTENSIONOBJECT_DECODED;
-        request.userIdentityToken.content.decoded.type =
-                &UA_TYPES[UA_TYPES_ANONYMOUSIDENTITYTOKEN];
-        request.userIdentityToken.content.decoded.data = identityToken;
-    } else {
-        UA_UserNameIdentityToken* identityToken =
-                UA_UserNameIdentityToken_new();
-        UA_UserNameIdentityToken_init (identityToken);
-        UA_String_copy(&client->token.policyId, &identityToken->policyId);
-        UA_String_copy(&client->username, &identityToken->userName);
-        UA_String_copy(&client->password, &identityToken->password);
+    /* If not token is set, use anonymous */
+    if(request.userIdentityToken.encoding == UA_EXTENSIONOBJECT_ENCODED_NOBODY) {
+        UA_AnonymousIdentityToken *t = UA_AnonymousIdentityToken_new();
+        if(!t) {
+            UA_ActivateSessionRequest_deleteMembers(&request);
+            return UA_STATUSCODE_BADOUTOFMEMORY;
+        }
+        request.userIdentityToken.content.decoded.data = t;
+        request.userIdentityToken.content.decoded.type = &UA_TYPES[UA_TYPES_ANONYMOUSIDENTITYTOKEN];
         request.userIdentityToken.encoding = UA_EXTENSIONOBJECT_DECODED;
-        request.userIdentityToken.content.decoded.type =
-                &UA_TYPES[UA_TYPES_USERNAMEIDENTITYTOKEN];
-        request.userIdentityToken.content.decoded.data = identityToken;
     }
+
+    /* Set the policy-Id from the endpoint. Every IdentityToken starts with a
+     * string. */
+    retval |= UA_String_copy(&client->config.userTokenPolicy.policyId,
+                             (UA_String*)request.userIdentityToken.content.decoded.data);
+
+    /* Encrypt the UserIdentityToken */
+    const UA_String *userTokenPolicy = &client->channel.securityPolicy->policyUri;
+    if(client->config.userTokenPolicy.securityPolicyUri.length > 0)
+        userTokenPolicy = &client->config.userTokenPolicy.securityPolicyUri;
+    retval |= encryptUserIdentityToken(client, userTokenPolicy, &request.userIdentityToken);
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_ActivateSessionRequest_deleteMembers(&request);
+        client->connectStatus = retval;
+        return retval;
+    }
+
     /* This function call is to prepare a client signature */
     if(client->channel.securityMode == UA_MESSAGESECURITYMODE_SIGN ||
        client->channel.securityMode == UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) {
         signActivateSessionRequest(&client->channel, &request);
     }
 
-    UA_StatusCode retval = UA_Client_sendAsyncRequest (
+    retval = UA_Client_sendAsyncRequest (
             client, &request, &UA_TYPES[UA_TYPES_ACTIVATESESSIONREQUEST],
             (UA_ClientAsyncServiceCallback) responseActivateSession,
             &UA_TYPES[UA_TYPES_ACTIVATESESSIONRESPONSE], NULL, requestId);
+
     UA_ActivateSessionRequest_deleteMembers(&request);
     client->connectStatus = retval;
     return retval;
@@ -409,7 +421,7 @@ responseGetEndpoints(UA_Client *client, void *userdata, UA_UInt32 requestId,
             continue;
 
         /* Look for an endpoint corresponding to the client security policy */
-        if(!UA_String_equal(&endpoint->securityPolicyUri, &client->securityPolicy.policyUri))
+        if(!UA_String_equal(&endpoint->securityPolicyUri, &client->channel.securityPolicy->policyUri))
             continue;
 
         endpointFound = true;
@@ -419,22 +431,32 @@ responseGetEndpoints(UA_Client *client, void *userdata, UA_UInt32 requestId,
             UA_UserTokenPolicy* userToken = &endpoint->userIdentityTokens[j];
 
             /* Usertokens also have a security policy... */
-            if(userToken->securityPolicyUri.length > 0
-                    && !UA_String_equal(&userToken->securityPolicyUri,
-                                         &securityNone))
+            if(userToken->securityPolicyUri.length > 0 &&
+               !UA_String_equal(&userToken->securityPolicyUri, &securityNone))
                 continue;
 
-            /* UA_CLIENTAUTHENTICATION_NONE == UA_USERTOKENTYPE_ANONYMOUS
-             * UA_CLIENTAUTHENTICATION_USERNAME == UA_USERTOKENTYPE_USERNAME
-             * TODO: Check equivalence for other types when adding the support */
-            if((int)client->authenticationMethod
-                    != (int)userToken->tokenType)
+            /* Does the token type match the client configuration? */
+            if((userToken->tokenType == UA_USERTOKENTYPE_ANONYMOUS &&
+                client->config.userIdentityToken.content.decoded.type !=
+                &UA_TYPES[UA_TYPES_ANONYMOUSIDENTITYTOKEN] &&
+                client->config.userIdentityToken.content.decoded.type != NULL) ||
+               (userToken->tokenType == UA_USERTOKENTYPE_USERNAME &&
+                client->config.userIdentityToken.content.decoded.type !=
+                &UA_TYPES[UA_TYPES_USERNAMEIDENTITYTOKEN]) ||
+               (userToken->tokenType == UA_USERTOKENTYPE_CERTIFICATE &&
+                client->config.userIdentityToken.content.decoded.type !=
+                &UA_TYPES[UA_TYPES_X509IDENTITYTOKEN]) ||
+               (userToken->tokenType == UA_USERTOKENTYPE_ISSUEDTOKEN &&
+                client->config.userIdentityToken.content.decoded.type !=
+                &UA_TYPES[UA_TYPES_ISSUEDIDENTITYTOKEN]))
                 continue;
 
             /* Endpoint with matching usertokenpolicy found */
             tokenFound = true;
-            UA_UserTokenPolicy_deleteMembers(&client->token);
-            UA_UserTokenPolicy_copy(userToken, &client->token);
+            UA_EndpointDescription_deleteMembers(&client->config.endpoint);
+            UA_EndpointDescription_copy(endpoint, &client->config.endpoint);
+            UA_UserTokenPolicy_deleteMembers(&client->config.userTokenPolicy);
+            UA_UserTokenPolicy_copy(userToken, &client->config.userTokenPolicy);
             break;
         }
     }
@@ -461,7 +483,7 @@ requestGetEndpoints(UA_Client *client, UA_UInt32 *requestId) {
     request.requestHeader.timestamp = UA_DateTime_now();
     request.requestHeader.timeoutHint = 10000;
     /* assume the endpointurl outlives the service call */
-    UA_String_copy (&client->endpointUrl, &request.endpointUrl);
+    UA_String_copy(&client->endpointUrl, &request.endpointUrl);
 
     client->connectStatus = UA_Client_sendAsyncRequest(
             client, &request, &UA_TYPES[UA_TYPES_GETENDPOINTSREQUEST],
@@ -510,7 +532,10 @@ requestSession(UA_Client *client, UA_UInt32 *requestId) {
     UA_ByteString_copy(&client->channel.localNonce, &request.clientNonce);
     request.requestedSessionTimeout = client->config.requestedSessionTimeout;
     request.maxResponseMessageSize = UA_INT32_MAX;
-    UA_String_copy(&client->endpointUrl, &request.endpointUrl);
+    UA_String_copy(&client->config.endpoint.endpointUrl, &request.endpointUrl);
+
+    UA_ApplicationDescription_copy(&client->config.clientDescription,
+                                   &request.clientDescription);
 
     retval = UA_Client_sendAsyncRequest (
             client, &request, &UA_TYPES[UA_TYPES_CREATESESSIONREQUEST],
@@ -569,11 +594,14 @@ UA_Client_connect_async(UA_Client *client, const char *endpointUrl,
     client->channel.sendSequenceNumber = 0;
     client->requestId = 0;
 
+    UA_String_deleteMembers(&client->endpointUrl);
+    client->endpointUrl = UA_STRING_ALLOC(endpointUrl);
+
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
-    client->connection = client->config.initConnectionFunc(
-            client->config.localConnectionConfig,
-            UA_STRING((char*)(uintptr_t)endpointUrl),
-            client->config.timeout, &client->config.logger);
+    client->connection =
+        client->config.initConnectionFunc(client->config.localConnectionConfig,
+                                          client->endpointUrl,
+                                          client->config.timeout, &client->config.logger);
     if(client->connection.state != UA_CONNECTION_OPENING) {
         UA_LOG_TRACE(&client->config.logger, UA_LOGCATEGORY_CLIENT,
                      "Could not init async connection");
@@ -581,22 +609,20 @@ UA_Client_connect_async(UA_Client *client, const char *endpointUrl,
         goto cleanup;
     }
 
-    UA_String_deleteMembers(&client->endpointUrl);
-    client->endpointUrl = UA_STRING_ALLOC(endpointUrl);
-    if(!client->endpointUrl.data) {
-        retval = UA_STATUSCODE_BADOUTOFMEMORY;
-        goto cleanup;
-    }
-
     /* Set the channel SecurityMode if not done so far */
     if(client->channel.securityMode == UA_MESSAGESECURITYMODE_INVALID)
         client->channel.securityMode = UA_MESSAGESECURITYMODE_NONE;
 
     /* Set the channel SecurityPolicy if not done so far */
     if(!client->channel.securityPolicy) {
+        UA_SecurityPolicy *sp =
+            getSecurityPolicy(client, UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#None"));
+        if(!sp) {
+            retval = UA_STATUSCODE_BADINTERNALERROR;
+            goto cleanup;
+        }
         UA_ByteString remoteCertificate = UA_BYTESTRING_NULL;
-        retval = UA_SecureChannel_setSecurityPolicy(&client->channel,
-                                                    &client->securityPolicy,
+        retval = UA_SecureChannel_setSecurityPolicy(&client->channel, sp,
                                                     &remoteCertificate);
         if(retval != UA_STATUSCODE_GOOD)
             goto cleanup;

+ 18 - 12
src/client/ua_client_discovery.c

@@ -15,18 +15,20 @@ UA_Client_getEndpoints(UA_Client *client, const char *serverUrl,
                        UA_EndpointDescription** endpointDescriptions) {
     UA_Boolean connected = (client->state > UA_CLIENTSTATE_DISCONNECTED);
     /* Client is already connected to a different server */
-    if(connected && strncmp((const char*)client->endpointUrl.data, serverUrl,
-                            client->endpointUrl.length) != 0) {
+    if(connected && strncmp((const char*)client->config.endpoint.endpointUrl.data, serverUrl,
+                            client->config.endpoint.endpointUrl.length) != 0) {
         return UA_STATUSCODE_BADINVALIDARGUMENT;
     }
 
     UA_StatusCode retval;
+    const UA_String url = UA_STRING((char*)(uintptr_t)serverUrl);
     if(!connected) {
-        retval = UA_Client_connectInternal(client, serverUrl, false, false);
+        retval = UA_Client_connectTCPSecureChannel(client, url);
         if(retval != UA_STATUSCODE_GOOD)
             return retval;
     }
-    retval = UA_Client_getEndpointsInternal(client, endpointDescriptionsSize, endpointDescriptions);
+    retval = UA_Client_getEndpointsInternal(client, url, endpointDescriptionsSize,
+                                            endpointDescriptions);
 
     if(!connected)
         UA_Client_disconnect(client);
@@ -41,13 +43,15 @@ UA_Client_findServers(UA_Client *client, const char *serverUrl,
                       UA_ApplicationDescription **registeredServers) {
     UA_Boolean connected = (client->state > UA_CLIENTSTATE_DISCONNECTED);
     /* Client is already connected to a different server */
-    if(connected && strncmp((const char*)client->endpointUrl.data, serverUrl,
-                            client->endpointUrl.length) != 0) {
+    if(connected && strncmp((const char*)client->config.endpoint.endpointUrl.data, serverUrl,
+                            client->config.endpoint.endpointUrl.length) != 0) {
         return UA_STATUSCODE_BADINVALIDARGUMENT;
     }
 
+    UA_StatusCode retval;
+    const UA_String url = UA_STRING((char*)(uintptr_t)serverUrl);
     if(!connected) {
-        UA_StatusCode retval = UA_Client_connectInternal(client, serverUrl, true, false);
+        retval = UA_Client_connectTCPSecureChannel(client, url);
         if(retval != UA_STATUSCODE_GOOD)
             return retval;
     }
@@ -66,7 +70,7 @@ UA_Client_findServers(UA_Client *client, const char *serverUrl,
                         &response, &UA_TYPES[UA_TYPES_FINDSERVERSRESPONSE]);
 
     /* Process the response */
-    UA_StatusCode retval = response.responseHeader.serviceResult;
+    retval = response.responseHeader.serviceResult;
     if(retval == UA_STATUSCODE_GOOD) {
         *registeredServersSize = response.serversSize;
         *registeredServers = response.servers;
@@ -93,13 +97,15 @@ UA_Client_findServersOnNetwork(UA_Client *client, const char *serverUrl,
                                size_t *serverOnNetworkSize, UA_ServerOnNetwork **serverOnNetwork) {
     UA_Boolean connected = (client->state > UA_CLIENTSTATE_DISCONNECTED);
     /* Client is already connected to a different server */
-    if(connected && strncmp((const char*)client->endpointUrl.data, serverUrl,
-                            client->endpointUrl.length) != 0) {
+    if(connected && strncmp((const char*)client->config.endpoint.endpointUrl.data, serverUrl,
+                            client->config.endpoint.endpointUrl.length) != 0) {
         return UA_STATUSCODE_BADINVALIDARGUMENT;
     }
 
+    UA_StatusCode retval;
+    const UA_String url = UA_STRING((char*)(uintptr_t)serverUrl);
     if(!connected) {
-        UA_StatusCode retval = UA_Client_connectInternal(client, serverUrl, true, false);
+        retval = UA_Client_connectTCPSecureChannel(client, url);
         if(retval != UA_STATUSCODE_GOOD)
             return retval;
     }
@@ -118,7 +124,7 @@ UA_Client_findServersOnNetwork(UA_Client *client, const char *serverUrl,
                         &response, &UA_TYPES[UA_TYPES_FINDSERVERSONNETWORKRESPONSE]);
 
     /* Process the response */
-    UA_StatusCode retval = response.responseHeader.serviceResult;
+    retval = response.responseHeader.serviceResult;
     if(retval == UA_STATUSCODE_GOOD) {
         *serverOnNetworkSize = response.serversSize;
         *serverOnNetwork = response.servers;

+ 25 - 18
src/client/ua_client_internal.h

@@ -126,11 +126,6 @@ typedef struct CustomCallback {
     const UA_DataType *outDataType;
 } CustomCallback;
 
-typedef enum {
-    UA_CLIENTAUTHENTICATION_NONE,
-    UA_CLIENTAUTHENTICATION_USERNAME
-} UA_Client_Authentication;
-
 struct UA_Client {
     /* State */
     UA_ClientState state;
@@ -141,25 +136,18 @@ struct UA_Client {
 
     /* Connection */
     UA_Connection connection;
-    UA_String endpointUrl;
 
     /* SecureChannel */
-    UA_SecurityPolicy securityPolicy; /* TODO: Move supported policies to the config */
     UA_SecureChannel channel;
     UA_UInt32 requestId;
     UA_DateTime nextChannelRenewal;
 
-    /* Authentication */
-    UA_Client_Authentication authenticationMethod;
-    UA_String username;
-    UA_String password;
-
     /* Session */
-    UA_UserTokenPolicy token;
     UA_NodeId authenticationToken;
     UA_UInt32 requestHandle;
 
     UA_Boolean endpointsHandshake;
+    UA_String endpointUrl; /* Only for the async connect */
 
     /* Async Service */
     AsyncServiceCall asyncConnectCall;
@@ -186,14 +174,21 @@ struct UA_Client {
 void
 setClientState(UA_Client *client, UA_ClientState state);
 
+/* The endpointUrl must be set in the configuration. If the complete
+ * endpointdescription is not set, a GetEndpoints is performed. */
+UA_StatusCode
+UA_Client_connectInternal(UA_Client *client, const UA_String endpointUrl);
+
+UA_StatusCode
+UA_Client_connectTCPSecureChannel(UA_Client *client, const UA_String endpointUrl);
+
 UA_StatusCode
-UA_Client_connectInternal(UA_Client *client, const char *endpointUrl,
-                          UA_Boolean endpointsHandshake, UA_Boolean createNewSession);
+UA_Client_connectSession(UA_Client *client);
 
 UA_StatusCode
-UA_Client_getEndpointsInternal(UA_Client *client,
-                               size_t* endpointDescriptionsSize,
-                               UA_EndpointDescription** endpointDescriptions);
+UA_Client_getEndpointsInternal(UA_Client *client, const UA_String endpointUrl,
+                               size_t *endpointDescriptionsSize,
+                               UA_EndpointDescription **endpointDescriptions);
 
 /* Receive and process messages until a synchronous message arrives or the
  * timout finishes */
@@ -223,6 +218,18 @@ receiveServiceResponseAsync(UA_Client *client, void *response,
 UA_StatusCode
 UA_Client_connect_iterate (UA_Client *client);
 
+void
+setUserIdentityPolicyId(const UA_EndpointDescription *endpoint,
+                        const UA_DataType *tokenType,
+                        UA_String *policyId, UA_String *securityPolicyUri);
+
+UA_SecurityPolicy *
+getSecurityPolicy(UA_Client *client, UA_String policyUri);
+
+UA_StatusCode
+encryptUserIdentityToken(UA_Client *client, const UA_String *userTokenSecurityPolicy,
+                         UA_ExtensionObject *userIdentityToken);
+
 _UA_END_DECLS
 
 #endif /* UA_CLIENT_INTERNAL_H_ */

+ 8 - 6
tests/encryption/check_encryption_basic128rsa15.c

@@ -86,8 +86,8 @@ START_TEST(encryption_connect) {
     size_t endpointArraySize = 0;
     UA_ByteString *trustList = NULL;
     size_t trustListSize = 0;
-    /* UA_ByteString *revocationList = NULL; */
-    /* size_t revocationListSize = 0; */
+    UA_ByteString *revocationList = NULL;
+    size_t revocationListSize = 0;
     UA_ByteString *remoteCertificate = NULL;
 
     /* Load certificate and private key */
@@ -143,10 +143,12 @@ START_TEST(encryption_connect) {
 
     /* Secure client initialization */
     client = UA_Client_new();
-    /* UA_ClientConfig *cc = UA_Client_getConfig(client); */
-    /* UA_ClientConfig_setDefaultEncryption(cc, certificate, privateKey, */
-    /*                                      trustList, trustListSize, */
-    /*                                      revocationList, revocationListSize); */
+    UA_ClientConfig *cc = UA_Client_getConfig(client);
+    UA_ClientConfig_setDefaultEncryption(cc, certificate, privateKey,
+                                         trustList, trustListSize,
+                                         revocationList, revocationListSize);
+    cc->securityPolicyUri =
+        UA_STRING_ALLOC("http://opcfoundation.org/UA/SecurityPolicy#Basic128Rsa15");
     ck_assert_msg(client != NULL);
 
     for(size_t deleteCount = 0; deleteCount < trustListSize; deleteCount++) {

+ 8 - 6
tests/encryption/check_encryption_basic256sha256.c

@@ -86,8 +86,8 @@ START_TEST(encryption_connect) {
     size_t endpointArraySize = 0;
     UA_ByteString *trustList = NULL;
     size_t trustListSize = 0;
-    /* UA_ByteString *revocationList = NULL; */
-    /* size_t revocationListSize = 0; */
+    UA_ByteString *revocationList = NULL;
+    size_t revocationListSize = 0;
     UA_ByteString *remoteCertificate = NULL;
 
     /* Load certificate and private key */
@@ -143,10 +143,12 @@ START_TEST(encryption_connect) {
 
     /* Secure client initialization */
     client = UA_Client_new();
-    /* UA_ClientConfig *cc = UA_Client_getConfig(client); */
-    /* UA_ClientConfig_setDefaultEncryption(cc, certificate, privateKey, */
-    /*                                      trustList, trustListSize, */
-    /*                                      revocationList, revocationListSize); */
+    UA_ClientConfig *cc = UA_Client_getConfig(client);
+    UA_ClientConfig_setDefaultEncryption(cc, certificate, privateKey,
+                                         trustList, trustListSize,
+                                         revocationList, revocationListSize);
+    cc->securityPolicyUri =
+        UA_STRING_ALLOC("http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256");
     ck_assert_msg(client != NULL);
 
     for(size_t deleteCount = 0; deleteCount < trustListSize; deleteCount++) {

+ 5 - 2
tests/fuzz/corpus_generator.c

@@ -564,7 +564,9 @@ int main(void) {
     emptyCorpusDir();
     start_server();
 
-    UA_Client *client = UA_Client_new(UA_ClientConfig_default);
+    UA_Client *client = UA_Client_new();
+    UA_ClientConfig_setDefault(UA_Client_getConfig(client));
+
     // this will also call getEndpointsRequest
     UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840");
     if(retval == UA_STATUSCODE_GOOD)
@@ -574,7 +576,8 @@ int main(void) {
 
     if(retval == UA_STATUSCODE_GOOD) {
         // now also connect with user/pass so that fuzzer also knows how to do that
-        client = UA_Client_new(UA_ClientConfig_default);
+        client = UA_Client_new();
+        UA_ClientConfig_setDefault(UA_Client_getConfig(client));
         retval = UA_Client_connect_username(client, "opc.tcp://localhost:4840", "user", "password");
         retval = retval == UA_STATUSCODE_BADUSERACCESSDENIED ? UA_STATUSCODE_GOOD : retval;
         UA_Client_disconnect(client);

+ 4 - 3
tests/server/check_discovery.c

@@ -274,12 +274,12 @@ static void
 FindOnNetworkAndCheck(UA_String expectedServerNames[], size_t expectedServerNamesSize,
                       const char *filterUri, const char *filterLocale,
                       const char** filterCapabilities, size_t filterCapabilitiesSize) {
-    UA_Client *client = UA_Client_new(UA_ClientConfig_default);
+    UA_Client *client = UA_Client_new();
+    UA_ClientConfig_setDefault(UA_Client_getConfig(client));
 
     UA_ServerOnNetwork* serverOnNetwork = NULL;
     size_t serverOnNetworkSize = 0;
 
-
     size_t  serverCapabilityFilterSize = 0;
     UA_String *serverCapabilityFilter = NULL;
 
@@ -363,7 +363,8 @@ GetEndpoints(UA_Client *client, const UA_String* endpointUrl,
 static void
 GetEndpointsAndCheck(const char* discoveryUrl, const char* filterTransportProfileUri,
                      const UA_String expectedEndpointUrls[], size_t expectedEndpointUrlsSize) {
-    UA_Client *client = UA_Client_new(UA_ClientConfig_default);
+    UA_Client *client = UA_Client_new();
+    UA_ClientConfig_setDefault(UA_Client_getConfig(client));
 
     ck_assert_uint_eq(UA_Client_connect(client, discoveryUrl), UA_STATUSCODE_GOOD);
 

+ 7 - 4
tests/server/check_subscription_events.c

@@ -184,11 +184,14 @@ setup(void) {
     addNewEventType();
     setupSelectClauses();
     THREAD_CREATE(server_thread, serverloop);
-    client = UA_Client_new(UA_ClientConfig_default);
+
+    client = UA_Client_new();
+    UA_ClientConfig_setDefault(UA_Client_getConfig(client));
+
     UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840");
-    if (retval != UA_STATUSCODE_GOOD)
-    {
-        fprintf(stderr, "Client can not connect to opc.tcp://localhost:4840. %s", UA_StatusCode_name(retval));
+    if(retval != UA_STATUSCODE_GOOD) {
+        fprintf(stderr, "Client can not connect to opc.tcp://localhost:4840. %s",
+                UA_StatusCode_name(retval));
         exit(1);
     }
     setupSubscription();