aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/client.c107
-rw-r--r--src/common.c150
-rw-r--r--src/common.h206
-rw-r--r--src/server_lds.c65
-rw-r--r--src/server_register.c167
5 files changed, 330 insertions, 365 deletions
diff --git a/src/client.c b/src/client.c
index f2166a6..ed8b12a 100644
--- a/src/client.c
+++ b/src/client.c
@@ -108,19 +108,15 @@ opGetEndpoints (UA_Client *client, const char *url)
/**
* Connects to a server and reads the current time node.
*
- * @param username Username for session auth, or NULL for anonymous.
- * @param password Password for session auth (ignored when username is NULL).
+ * Authentication (anonymous, username/password, or X509 certificate) is
+ * configured in the client config before this function is called.
+ *
* @return EXIT_SUCCESS on success, EXIT_FAILURE otherwise.
*/
static int
-opReadTime (UA_Client *client, const char *url, const char *username,
- const char *password)
+opReadTime (UA_Client *client, const char *url)
{
- UA_StatusCode retval;
- if (username)
- retval = UA_Client_connectUsername (client, url, username, password);
- else
- retval = UA_Client_connect (client, url);
+ UA_StatusCode retval = UA_Client_connect (client, url);
if (retval != UA_STATUSCODE_GOOD)
{
@@ -212,34 +208,20 @@ main (int argc, char **argv)
return EXIT_FAILURE;
}
- /* Security configuration (optional). When certificate, privateKey, and
- trustStore are all omitted the client connects without encryption.
- When any of the three is present, all three are required. */
- const char *certPath = configGet (&cfg, "certificate");
- const char *keyPath = configGet (&cfg, "privateKey");
- const char *trustStore = configGet (&cfg, "trustStore");
- UA_Boolean secure
- = (certPath != NULL || keyPath != NULL || trustStore != NULL);
-
- if (secure && (!certPath || !keyPath || !trustStore))
+ SecurityConfig sec;
+ if (parseSecurityConfig (&cfg, "Client", true, &sec) != 0)
{
- UA_LOG_FATAL (UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
- "Incomplete security config: certificate, privateKey, and "
- "trustStore must all be set, or all omitted");
configFree (&cfg);
return EXIT_FAILURE;
}
/* ---- Auth config (read-time only) ---- */
- const char *username = NULL, *password = NULL;
- UA_Boolean certAuth = false;
+ AuthConfig auth = { .mode = AUTH_ANONYMOUS };
- if (op == OP_READ_TIME
- && parseAuthConfig (&cfg, "Client", NULL, &username, &password,
- &certAuth)
- != 0)
+ if (op == OP_READ_TIME && parseAuthConfig (&cfg, "Client", &auth) != 0)
{
+ freeTrustStore (sec.trustPaths, sec.trustSize);
configFree (&cfg);
return EXIT_FAILURE;
}
@@ -247,62 +229,21 @@ main (int argc, char **argv)
/* ---- Create client ---- */
UA_Client *client = UA_Client_new ();
- char **trustPaths = NULL;
- size_t trustSize = 0;
- if (secure)
- {
- const char *secModeStr = configRequire (&cfg, "securityMode", "Client");
- const char *secPolStr = configRequire (&cfg, "securityPolicy", "Client");
- if (!secModeStr || !secPolStr)
- {
- UA_Client_delete (client);
- configFree (&cfg);
- return EXIT_FAILURE;
- }
-
- UA_MessageSecurityMode secMode = parseSecurityMode (secModeStr);
- if (secMode == UA_MESSAGESECURITYMODE_INVALID)
- {
- UA_LOG_FATAL (UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
- "Unknown security mode: %s", secModeStr);
- UA_Client_delete (client);
- configFree (&cfg);
- return EXIT_FAILURE;
- }
-
- const char *secPolUri = resolveSecurityPolicyUri (secPolStr);
- if (!secPolUri)
- {
- UA_LOG_FATAL (UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
- "Unknown security policy: %s", secPolStr);
- UA_Client_delete (client);
- configFree (&cfg);
- return EXIT_FAILURE;
- }
-
- if (loadTrustStore (trustStore, &trustPaths, &trustSize) != 0)
- {
- UA_Client_delete (client);
- configFree (&cfg);
- return EXIT_FAILURE;
- }
-
- UA_StatusCode retval = createSecureClientConfig (
- UA_Client_getConfig (client), applicationUri, certPath, keyPath,
- trustPaths, trustSize, secMode, secPolUri, certAuth);
- if (retval != UA_STATUSCODE_GOOD)
- {
- UA_Client_delete (client);
- freeTrustStore (trustPaths, trustSize);
- configFree (&cfg);
- return EXIT_FAILURE;
- }
- }
+ UA_StatusCode retval;
+ if (sec.certPath)
+ retval = createSecureClientConfig (UA_Client_getConfig (client),
+ applicationUri, &sec, &auth);
else
+ retval = createUnsecureClientConfig (UA_Client_getConfig (client),
+ applicationUri, &auth);
+
+ if (retval != UA_STATUSCODE_GOOD)
{
- createUnsecureClientConfig (UA_Client_getConfig (client),
- applicationUri);
+ UA_Client_delete (client);
+ freeTrustStore (sec.trustPaths, sec.trustSize);
+ configFree (&cfg);
+ return EXIT_FAILURE;
}
UA_Client_getConfig (client)->logging->context = (void *)(uintptr_t)logLevel;
@@ -319,7 +260,7 @@ main (int argc, char **argv)
rc = opGetEndpoints (client, endpointUrl);
break;
case OP_READ_TIME:
- rc = opReadTime (client, endpointUrl, username, password);
+ rc = opReadTime (client, endpointUrl);
break;
default:
rc = EXIT_FAILURE;
@@ -329,7 +270,7 @@ main (int argc, char **argv)
/* ---- Cleanup ---- */
UA_Client_delete (client);
- freeTrustStore (trustPaths, trustSize);
+ freeTrustStore (sec.trustPaths, sec.trustSize);
configFree (&cfg);
return rc;
diff --git a/src/common.c b/src/common.c
index 865fc55..cf364bb 100644
--- a/src/common.c
+++ b/src/common.c
@@ -172,43 +172,33 @@ parseLogLevel (const char *name)
}
int
-parseAuthConfig (const Config *cfg, const char *program,
- UA_Boolean *allowAnonymous, const char **username,
- const char **password, UA_Boolean *certAuth)
+parseAuthConfig (const Config *cfg, const char *program, AuthConfig *auth)
{
const char *authMode = configRequire (cfg, "authMode", program);
if (!authMode)
return -1;
- *username = NULL;
- *password = NULL;
- if (certAuth)
- *certAuth = false;
+ memset (auth, 0, sizeof (*auth));
if (strcmp (authMode, "anonymous") == 0)
{
- if (allowAnonymous)
- *allowAnonymous = true;
+ auth->mode = AUTH_ANONYMOUS;
return 0;
}
if (strcmp (authMode, "user") == 0)
{
- if (allowAnonymous)
- *allowAnonymous = false;
- *username = configRequire (cfg, "username", program);
- *password = configRequire (cfg, "password", program);
- if (!*username || !*password)
+ auth->mode = AUTH_USER;
+ auth->user.username = configRequire (cfg, "username", program);
+ auth->user.password = configRequire (cfg, "password", program);
+ if (!auth->user.username || !auth->user.password)
return -1;
return 0;
}
if (strcmp (authMode, "cert") == 0)
{
- if (allowAnonymous)
- *allowAnonymous = false;
- if (certAuth)
- *certAuth = true;
+ auth->mode = AUTH_CERT;
return 0;
}
@@ -219,6 +209,63 @@ parseAuthConfig (const Config *cfg, const char *program,
return -1;
}
+int
+parseSecurityConfig (const Config *cfg, const char *program,
+ UA_Boolean needsModePolicy, SecurityConfig *sec)
+{
+ memset (sec, 0, sizeof (*sec));
+
+ const char *certPath = configGet (cfg, "certificate");
+ const char *keyPath = configGet (cfg, "privateKey");
+ const char *trustStore = configGet (cfg, "trustStore");
+ UA_Boolean secure
+ = (certPath != NULL || keyPath != NULL || trustStore != NULL);
+
+ if (!secure)
+ return 0;
+
+ if (!certPath || !keyPath || !trustStore)
+ {
+ UA_LOG_FATAL (UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
+ "%s: incomplete security config: certificate, privateKey, "
+ "and trustStore must all be set, or all omitted",
+ program);
+ return -1;
+ }
+
+ sec->certPath = certPath;
+ sec->keyPath = keyPath;
+
+ if (needsModePolicy)
+ {
+ const char *secModeStr = configRequire (cfg, "securityMode", program);
+ const char *secPolStr = configRequire (cfg, "securityPolicy", program);
+ if (!secModeStr || !secPolStr)
+ return -1;
+
+ sec->securityMode = parseSecurityMode (secModeStr);
+ if (sec->securityMode == UA_MESSAGESECURITYMODE_INVALID)
+ {
+ UA_LOG_FATAL (UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
+ "%s: unknown security mode: %s", program, secModeStr);
+ return -1;
+ }
+
+ sec->securityPolicyUri = resolveSecurityPolicyUri (secPolStr);
+ if (!sec->securityPolicyUri)
+ {
+ UA_LOG_FATAL (UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
+ "%s: unknown security policy: %s", program, secPolStr);
+ return -1;
+ }
+ }
+
+ if (loadTrustStore (trustStore, &sec->trustPaths, &sec->trustSize) != 0)
+ return -1;
+
+ return 0;
+}
+
UA_MessageSecurityMode
parseSecurityMode (const char *name)
{
@@ -351,26 +398,26 @@ printEndpoint (const UA_EndpointDescription *endpoint, size_t index)
* ======================================================================== */
UA_Server *
-createServer (UA_UInt16 port, const char *applicationUri, const char *certPath,
- const char *keyPath, char **trustPaths, size_t trustSize,
- UA_Boolean discovery, UA_StatusCode *retval)
+createServer (UA_UInt16 port, const char *applicationUri,
+ const SecurityConfig *sec, UA_Boolean discovery,
+ UA_StatusCode *retval)
{
UA_Server *server = UA_Server_new ();
UA_ServerConfig *config = UA_Server_getConfig (server);
- if (certPath)
+ if (sec && sec->certPath)
{
- UA_ByteString certificate = loadFile (certPath);
- UA_ByteString privateKey = loadFile (keyPath);
+ UA_ByteString certificate = loadFile (sec->certPath);
+ UA_ByteString privateKey = loadFile (sec->keyPath);
/* +1: UA_STACKARRAY requires a strictly positive size for VLA. */
- UA_STACKARRAY (UA_ByteString, trustList, trustSize + 1);
- for (size_t i = 0; i < trustSize; i++)
- trustList[i] = loadFile (trustPaths[i]);
+ UA_STACKARRAY (UA_ByteString, trustList, sec->trustSize + 1);
+ for (size_t i = 0; i < sec->trustSize; i++)
+ trustList[i] = loadFile (sec->trustPaths[i]);
*retval = UA_ServerConfig_setDefaultWithSecureSecurityPolicies (
- config, port, &certificate, &privateKey, trustList, trustSize, NULL,
- 0, NULL, 0);
+ config, port, &certificate, &privateKey, trustList, sec->trustSize,
+ NULL, 0, NULL, 0);
/* When discovery is true (LDS) add SecurityPolicy#None
restricted to discovery services so that unencrypted clients
@@ -390,7 +437,7 @@ createServer (UA_UInt16 port, const char *applicationUri, const char *certPath,
UA_ByteString_clear (&certificate);
UA_ByteString_clear (&privateKey);
- for (size_t i = 0; i < trustSize; i++)
+ for (size_t i = 0; i < sec->trustSize; i++)
UA_ByteString_clear (&trustList[i]);
}
else
@@ -412,8 +459,16 @@ createServer (UA_UInt16 port, const char *applicationUri, const char *certPath,
}
UA_StatusCode
-createUnsecureClientConfig (UA_ClientConfig *cc, const char *applicationUri)
+createUnsecureClientConfig (UA_ClientConfig *cc, const char *applicationUri,
+ const AuthConfig *auth)
{
+ if (auth && auth->mode == AUTH_CERT)
+ {
+ UA_LOG_ERROR (UA_Log_Stdout, UA_LOGCATEGORY_APPLICATION,
+ "Certificate authentication requires encryption");
+ return UA_STATUSCODE_BADINVALIDARGUMENT;
+ }
+
UA_StatusCode retval = UA_ClientConfig_setDefault (cc);
if (retval != UA_STATUSCODE_GOOD)
return retval;
@@ -426,38 +481,39 @@ createUnsecureClientConfig (UA_ClientConfig *cc, const char *applicationUri)
cc->securityPolicyUri = UA_String_fromChars (
"http://opcfoundation.org/UA/SecurityPolicy#None");
+ if (auth && auth->mode == AUTH_USER)
+ UA_ClientConfig_setAuthenticationUsername (cc, auth->user.username,
+ auth->user.password);
+
return UA_STATUSCODE_GOOD;
}
UA_StatusCode
createSecureClientConfig (UA_ClientConfig *cc, const char *applicationUri,
- const char *certPath, const char *keyPath,
- char **trustPaths, size_t trustSize,
- UA_MessageSecurityMode securityMode,
- const char *securityPolicyUri, UA_Boolean certAuth)
+ const SecurityConfig *sec, const AuthConfig *auth)
{
- UA_ByteString certificate = loadFile (certPath);
- UA_ByteString privateKey = loadFile (keyPath);
+ UA_ByteString certificate = loadFile (sec->certPath);
+ UA_ByteString privateKey = loadFile (sec->keyPath);
/* +1: UA_STACKARRAY requires a strictly positive size for VLA. */
- UA_STACKARRAY (UA_ByteString, trustList, trustSize + 1);
- for (size_t i = 0; i < trustSize; i++)
- trustList[i] = loadFile (trustPaths[i]);
+ UA_STACKARRAY (UA_ByteString, trustList, sec->trustSize + 1);
+ for (size_t i = 0; i < sec->trustSize; i++)
+ trustList[i] = loadFile (sec->trustPaths[i]);
UA_StatusCode retval = UA_ClientConfig_setDefaultEncryption (
- cc, certificate, privateKey, trustList, trustSize, NULL, 0);
+ cc, certificate, privateKey, trustList, sec->trustSize, NULL, 0);
/* X509 identity token: reuse the application certificate. open62541
requires that the identity cert matches the SecureChannel cert, so
a separate user cert cannot be used. Call before clearing the local
buffers since setAuthenticationCert makes its own copy. */
- if (retval == UA_STATUSCODE_GOOD && certAuth)
+ if (retval == UA_STATUSCODE_GOOD && auth && auth->mode == AUTH_CERT)
retval
= UA_ClientConfig_setAuthenticationCert (cc, certificate, privateKey);
UA_ByteString_clear (&certificate);
UA_ByteString_clear (&privateKey);
- for (size_t i = 0; i < trustSize; i++)
+ for (size_t i = 0; i < sec->trustSize; i++)
UA_ByteString_clear (&trustList[i]);
if (retval != UA_STATUSCODE_GOOD)
@@ -471,8 +527,12 @@ createSecureClientConfig (UA_ClientConfig *cc, const char *applicationUri,
UA_String_clear (&cc->clientDescription.applicationUri);
cc->clientDescription.applicationUri = UA_String_fromChars (applicationUri);
- cc->securityMode = securityMode;
- cc->securityPolicyUri = UA_String_fromChars (securityPolicyUri);
+ cc->securityMode = sec->securityMode;
+ cc->securityPolicyUri = UA_String_fromChars (sec->securityPolicyUri);
+
+ if (auth && auth->mode == AUTH_USER)
+ UA_ClientConfig_setAuthenticationUsername (cc, auth->user.username,
+ auth->user.password);
return retval;
}
diff --git a/src/common.h b/src/common.h
index aff6ff4..b8643d7 100644
--- a/src/common.h
+++ b/src/common.h
@@ -17,6 +17,66 @@
#include "config.h"
+/* ========================================================================
+ * Aggregate Types
+ * ======================================================================== */
+
+/**
+ * @brief Session-level authentication mode.
+ */
+typedef enum
+{
+ AUTH_ANONYMOUS,
+ AUTH_USER,
+ AUTH_CERT
+} AuthMode;
+
+/**
+ * @brief Session-level authentication configuration (tagged union).
+ *
+ * AUTH_ANONYMOUS carries no payload. AUTH_USER carries borrowed pointers
+ * to username/password strings (owned by the Config that was parsed).
+ * AUTH_CERT carries no payload — the application certificate is reused
+ * as the X509 identity token.
+ */
+typedef struct
+{
+ AuthMode mode;
+ union
+ {
+ struct
+ {
+ const char *username;
+ const char *password;
+ } user;
+ };
+} AuthConfig;
+
+/**
+ * @brief Transport-level security configuration.
+ *
+ * Groups the certificate, private key, trust list, security mode, and
+ * security policy URI. All pointers are borrowed (owned by Config or
+ * returned by loadTrustStore / resolveSecurityPolicyUri). The caller
+ * must free trustPaths with freeTrustStore() when done.
+ *
+ * securityMode and securityPolicyUri are only meaningful for client
+ * configs; server-side callers may leave them zeroed.
+ */
+typedef struct
+{
+ const char *certPath;
+ const char *keyPath;
+ char **trustPaths;
+ size_t trustSize;
+ UA_MessageSecurityMode securityMode;
+ const char *securityPolicyUri;
+} SecurityConfig;
+
+/* ========================================================================
+ * File Loading
+ * ======================================================================== */
+
/**
* @brief Loads a DER-encoded certificate or key file into a UA_ByteString.
*
@@ -48,34 +108,9 @@ int loadTrustStore (const char *dirPath, char ***outPaths, size_t *outSize);
*/
void freeTrustStore (char **paths, size_t size);
-/**
- * @brief Creates a UA_Server, optionally configured with security policies.
- *
- * When @p certPath is non-NULL the server is initialized with encryption
- * (certificate, private key, trustlist). When @p discovery is true the
- * server additionally offers SecurityPolicy#None restricted to discovery
- * services (FindServers, GetEndpoints) so that unencrypted clients can
- * still discover the server. When @p discovery is false the server is
- * purely secure — no None security policy, no None endpoint. When
- * @p certPath is NULL the server runs with SecurityPolicy#None only
- * (keyPath, trustPaths and discovery are ignored). The applicationUri
- * is set in both cases.
- *
- * @param port Server port number.
- * @param applicationUri OPC UA application URI.
- * @param certPath Path to server certificate (.der), or NULL for unsecure.
- * @param keyPath Path to private key (.der), or NULL when certPath is NULL.
- * @param trustPaths Array of trustlist file paths (may be NULL).
- * @param trustSize Number of entries in trustPaths.
- * @param discovery When true and certPath is non-NULL, add a None
- * endpoint restricted to discovery services.
- * @param retval Output parameter set to the status code on failure.
- * @return A configured UA_Server, or NULL on error.
- */
-UA_Server *createServer (UA_UInt16 port, const char *applicationUri,
- const char *certPath, const char *keyPath,
- char **trustPaths, size_t trustSize,
- UA_Boolean discovery, UA_StatusCode *retval);
+/* ========================================================================
+ * Parsing Helpers
+ * ======================================================================== */
/**
* @brief Parses a log-level name into the corresponding UA_LogLevel value.
@@ -91,25 +126,37 @@ int parseLogLevel (const char *name);
/**
* @brief Parses the authMode key from a configuration file.
*
- * When authMode is "anonymous", sets *allowAnonymous to true and leaves
- * *username / *password as NULL. When authMode is "user", sets
- * *allowAnonymous to false and loads the username/password keys. When
- * authMode is "cert", sets *allowAnonymous to false and *certAuth to true.
+ * Populates an AuthConfig struct. When authMode is "anonymous", sets
+ * mode to AUTH_ANONYMOUS. When "user", sets mode to AUTH_USER and reads
+ * the username/password keys. When "cert", sets mode to AUTH_CERT.
* Logs errors internally.
*
- * @param cfg Parsed configuration.
- * @param program Program name (for error messages).
- * @param allowAnonymous Output: true for anonymous, false otherwise.
- * May be NULL (ignored — useful for client callers).
- * @param username Output: username string (owned by cfg), or NULL.
- * @param password Output: password string (owned by cfg), or NULL.
- * @param certAuth Output: true when authMode is "cert", false otherwise.
- * May be NULL (ignored — useful for server callers).
+ * @param cfg Parsed configuration.
+ * @param program Program name (for error messages).
+ * @param auth Output: populated AuthConfig.
+ * @return 0 on success, -1 on error.
+ */
+int parseAuthConfig (const Config *cfg, const char *program, AuthConfig *auth);
+
+/**
+ * @brief Parses security configuration from a config file.
+ *
+ * Reads certificate, privateKey, and trustStore keys. When all three
+ * are omitted, zeroes @p sec and returns 0 (unsecure). When any of the
+ * three is present, all three are required. When @p needsModePolicy is
+ * true, also reads and resolves securityMode and securityPolicy keys.
+ * Calls loadTrustStore() internally; the caller must free
+ * sec->trustPaths with freeTrustStore().
+ *
+ * @param cfg Parsed configuration.
+ * @param program Program name (for error messages).
+ * @param needsModePolicy When true, require securityMode and
+ * securityPolicy keys (client configs).
+ * @param sec Output: populated SecurityConfig.
* @return 0 on success, -1 on error.
*/
-int parseAuthConfig (const Config *cfg, const char *program,
- UA_Boolean *allowAnonymous, const char **username,
- const char **password, UA_Boolean *certAuth);
+int parseSecurityConfig (const Config *cfg, const char *program,
+ UA_Boolean needsModePolicy, SecurityConfig *sec);
/**
* @brief Parses a security mode name into the corresponding enum value.
@@ -133,49 +180,76 @@ UA_MessageSecurityMode parseSecurityMode (const char *name);
*/
const char *resolveSecurityPolicyUri (const char *shortName);
+/* ========================================================================
+ * Factory Functions
+ * ======================================================================== */
+
+/**
+ * @brief Creates a UA_Server, optionally configured with security policies.
+ *
+ * When @p sec is non-NULL the server is initialized with encryption
+ * (certificate, private key, trustlist). When @p discovery is true the
+ * server additionally offers SecurityPolicy#None restricted to discovery
+ * services (FindServers, GetEndpoints) so that unencrypted clients can
+ * still discover the server. When @p discovery is false the server is
+ * purely secure — no None security policy, no None endpoint. When
+ * @p sec is NULL the server runs with SecurityPolicy#None only
+ * (discovery is ignored). The applicationUri is set in both cases.
+ *
+ * @param port Server port number.
+ * @param applicationUri OPC UA application URI.
+ * @param sec Security configuration, or NULL for unsecure.
+ * @param discovery When true and sec is non-NULL, add a None
+ * endpoint restricted to discovery services.
+ * @param retval Output parameter set to the status code on failure.
+ * @return A configured UA_Server, or NULL on error.
+ */
+UA_Server *createServer (UA_UInt16 port, const char *applicationUri,
+ const SecurityConfig *sec, UA_Boolean discovery,
+ UA_StatusCode *retval);
+
/**
* @brief Initializes a UA_ClientConfig without encryption.
*
* Sets up a default client config with SecurityPolicy#None and the given
* application URI. Explicitly sets securityMode and securityPolicyUri so
- * that internal endpoint negotiation matches None endpoints.
+ * that internal endpoint negotiation matches None endpoints. When @p auth
+ * is non-NULL and mode is AUTH_USER, configures username/password
+ * authentication. AUTH_CERT returns an error (requires encryption).
*
* @param cc Pointer to a zero-initialized UA_ClientConfig.
* @param applicationUri OPC UA application URI.
+ * @param auth Authentication config, or NULL for anonymous.
* @return UA_STATUSCODE_GOOD on success, error code otherwise.
*/
UA_StatusCode createUnsecureClientConfig (UA_ClientConfig *cc,
- const char *applicationUri);
+ const char *applicationUri,
+ const AuthConfig *auth);
/**
- * @brief Initializes a UA_ClientConfig with encryption from file paths.
+ * @brief Initializes a UA_ClientConfig with encryption.
*
* The config must be zero-initialized by the caller before calling this
- * function. Loads the certificate, private key, and trustlist, then applies
- * default encryption settings. When @p certAuth is true, also configures
- * X509 certificate identity-token authentication using the same application
- * certificate (mutually exclusive with username/password authentication).
+ * function. Loads the certificate, private key, and trustlist, then
+ * applies default encryption settings. When @p auth is non-NULL:
+ * AUTH_CERT configures X509 certificate identity-token authentication
+ * using the same application certificate; AUTH_USER configures
+ * username/password authentication. Both are mutually exclusive.
*
* @param cc Pointer to a zero-initialized UA_ClientConfig.
* @param applicationUri OPC UA application URI.
- * @param certPath Path to client certificate (.der).
- * @param keyPath Path to private key (.der).
- * @param trustPaths Array of trustlist file paths (may be NULL if trustSize is
- * 0).
- * @param trustSize Number of entries in trustPaths.
- * @param securityMode Requested message security mode.
- * @param securityPolicyUri Security policy URI string (e.g.
- * "http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256").
- * @param certAuth When true, use the application certificate as X509 identity
- * token.
+ * @param sec Security configuration (cert, key, trust, mode, policy).
+ * @param auth Authentication config, or NULL for anonymous.
* @return UA_STATUSCODE_GOOD on success, error code otherwise.
*/
-UA_StatusCode
-createSecureClientConfig (UA_ClientConfig *cc, const char *applicationUri,
- const char *certPath, const char *keyPath,
- char **trustPaths, size_t trustSize,
- UA_MessageSecurityMode securityMode,
- const char *securityPolicyUri, UA_Boolean certAuth);
+UA_StatusCode createSecureClientConfig (UA_ClientConfig *cc,
+ const char *applicationUri,
+ const SecurityConfig *sec,
+ const AuthConfig *auth);
+
+/* ========================================================================
+ * Output Formatting
+ * ======================================================================== */
/**
* @brief Logs a UA_ApplicationDescription (server info from FindServers).
diff --git a/src/server_lds.c b/src/server_lds.c
index 3307073..311be4b 100644
--- a/src/server_lds.c
+++ b/src/server_lds.c
@@ -67,24 +67,6 @@ main (int argc, char *argv[])
return EXIT_FAILURE;
}
- /* Security configuration (optional). When certificate, privateKey, and
- trustStore are all omitted the server runs with SecurityPolicy#None
- only. When any of the three is present, all three are required. */
- const char *certPath = configGet (&cfg, "certificate");
- const char *keyPath = configGet (&cfg, "privateKey");
- const char *trustStore = configGet (&cfg, "trustStore");
- UA_Boolean secure
- = (certPath != NULL || keyPath != NULL || trustStore != NULL);
-
- if (secure && (!certPath || !keyPath || !trustStore))
- {
- UA_LOG_FATAL (UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
- "Incomplete security config: certificate, privateKey, and "
- "trustStore must all be set, or all omitted");
- configFree (&cfg);
- return EXIT_FAILURE;
- }
-
/* The OPC UA specification requires the cleanup timeout to exceed the
register-server interval. open62541 enforces a floor of 10 seconds. */
if (cleanupTimeout <= 10)
@@ -96,31 +78,27 @@ main (int argc, char *argv[])
return EXIT_FAILURE;
}
- UA_Boolean allowAnonymous;
- const char *username = NULL, *password = NULL;
- if (parseAuthConfig (&cfg, "ServerLDS", &allowAnonymous, &username,
- &password, NULL)
- != 0)
+ SecurityConfig sec;
+ if (parseSecurityConfig (&cfg, "ServerLDS", false, &sec) != 0)
{
configFree (&cfg);
return EXIT_FAILURE;
}
- char **trustPaths = NULL;
- size_t trustSize = 0;
- if (secure && loadTrustStore (trustStore, &trustPaths, &trustSize) != 0)
+ AuthConfig auth;
+ if (parseAuthConfig (&cfg, "ServerLDS", &auth) != 0)
{
+ freeTrustStore (sec.trustPaths, sec.trustSize);
configFree (&cfg);
return EXIT_FAILURE;
}
UA_StatusCode retval;
- UA_Server *server
- = createServer ((UA_UInt16)port, applicationUri, certPath, keyPath,
- trustPaths, trustSize, true, &retval);
+ UA_Server *server = createServer ((UA_UInt16)port, applicationUri,
+ sec.certPath ? &sec : NULL, true, &retval);
if (!server)
{
- freeTrustStore (trustPaths, trustSize);
+ freeTrustStore (sec.trustPaths, sec.trustSize);
configFree (&cfg);
return EXIT_FAILURE;
}
@@ -136,21 +114,28 @@ main (int argc, char *argv[])
/* Configure access control. UA_ServerConfig_setDefaultWithSecure-
SecurityPolicies sets certificate-only auth by default, so we must
always call UA_AccessControl_default to get the desired policy. */
- if (allowAnonymous)
+ switch (auth.mode)
{
+ case AUTH_ANONYMOUS:
retval = UA_AccessControl_default (serverConfig, true, NULL, 0, NULL);
- }
- else
- {
- UA_UsernamePasswordLogin logins[1];
- logins[0].username = UA_STRING ((char *)username);
- logins[0].password = UA_STRING ((char *)password);
- retval = UA_AccessControl_default (serverConfig, false, NULL, 1, logins);
+ break;
+ case AUTH_USER:
+ {
+ UA_UsernamePasswordLogin logins[1];
+ logins[0].username = UA_STRING ((char *)auth.user.username);
+ logins[0].password = UA_STRING ((char *)auth.user.password);
+ retval
+ = UA_AccessControl_default (serverConfig, false, NULL, 1, logins);
+ break;
+ }
+ case AUTH_CERT:
+ retval = UA_AccessControl_default (serverConfig, false, NULL, 0, NULL);
+ break;
}
if (retval != UA_STATUSCODE_GOOD)
{
UA_Server_delete (server);
- freeTrustStore (trustPaths, trustSize);
+ freeTrustStore (sec.trustPaths, sec.trustSize);
configFree (&cfg);
return EXIT_FAILURE;
}
@@ -166,7 +151,7 @@ main (int argc, char *argv[])
retval = UA_Server_run (server, &running);
UA_Server_delete (server);
- freeTrustStore (trustPaths, trustSize);
+ freeTrustStore (sec.trustPaths, sec.trustSize);
configFree (&cfg);
return retval == UA_STATUSCODE_GOOD ? EXIT_SUCCESS : EXIT_FAILURE;
}
diff --git a/src/server_register.c b/src/server_register.c
index 705fc18..8a64d08 100644
--- a/src/server_register.c
+++ b/src/server_register.c
@@ -41,16 +41,9 @@ stopHandler (int sign)
typedef struct
{
const char *appUri;
- const char *certPath;
- const char *keyPath;
- char **trustPaths;
- size_t trustSize;
- UA_MessageSecurityMode securityMode;
- const char *securityPolicyUri;
+ SecurityConfig sec;
+ AuthConfig auth;
int logLevel;
- const char *username;
- const char *password;
- UA_Boolean certAuth;
} LdsClientParams;
/**
@@ -64,21 +57,13 @@ makeLdsClientConfig (UA_ClientConfig *cc, const LdsClientParams *p)
{
memset (cc, 0, sizeof (UA_ClientConfig));
UA_StatusCode rv;
- if (p->certPath)
- {
- rv = createSecureClientConfig (
- cc, p->appUri, p->certPath, p->keyPath, p->trustPaths, p->trustSize,
- p->securityMode, p->securityPolicyUri, p->certAuth);
- }
+ if (p->sec.certPath)
+ rv = createSecureClientConfig (cc, p->appUri, &p->sec, &p->auth);
else
- {
- rv = createUnsecureClientConfig (cc, p->appUri);
- }
+ rv = createUnsecureClientConfig (cc, p->appUri, &p->auth);
if (rv != UA_STATUSCODE_GOOD)
return rv;
cc->logging->context = (void *)(uintptr_t)p->logLevel;
- if (p->username)
- UA_ClientConfig_setAuthenticationUsername (cc, p->username, p->password);
return UA_STATUSCODE_GOOD;
}
@@ -119,10 +104,8 @@ main (int argc, char **argv)
int rc = EXIT_FAILURE;
Config serverCfg = { 0 };
Config clientCfg = { 0 };
- char **serverTrustPaths = NULL;
- size_t serverTrustSize = 0;
- char **clientTrustPaths = NULL;
- size_t clientTrustSize = 0;
+ SecurityConfig serverSec = { 0 };
+ SecurityConfig clientSec = { 0 };
UA_Server *server = NULL;
if (configLoad (argv[1], &serverCfg) != 0)
@@ -137,34 +120,12 @@ main (int argc, char **argv)
if (!applicationUri || port < 0 || registerInterval < 0)
goto cleanup;
- /* Security configuration (optional). When certificate, privateKey, and
- trustStore are all omitted the server runs with SecurityPolicy#None
- only. When any of the three is present, all three are required. */
- const char *serverCertPath = configGet (&serverCfg, "certificate");
- const char *serverKeyPath = configGet (&serverCfg, "privateKey");
- const char *serverTrustStore = configGet (&serverCfg, "trustStore");
- UA_Boolean serverSecure = (serverCertPath != NULL || serverKeyPath != NULL
- || serverTrustStore != NULL);
-
- if (serverSecure && (!serverCertPath || !serverKeyPath || !serverTrustStore))
- {
- UA_LOG_FATAL (UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
- "Incomplete server security config: certificate, "
- "privateKey, and trustStore must all be set, or all "
- "omitted");
- goto cleanup;
- }
-
- UA_Boolean serverAllowAnonymous;
- const char *serverUsername = NULL, *serverPassword = NULL;
- if (parseAuthConfig (&serverCfg, "ServerRegister", &serverAllowAnonymous,
- &serverUsername, &serverPassword, NULL)
+ if (parseSecurityConfig (&serverCfg, "ServerRegister", false, &serverSec)
!= 0)
goto cleanup;
- if (serverSecure
- && loadTrustStore (serverTrustStore, &serverTrustPaths, &serverTrustSize)
- != 0)
+ AuthConfig serverAuth;
+ if (parseAuthConfig (&serverCfg, "ServerRegister", &serverAuth) != 0)
goto cleanup;
/* ── Load client config ─────────────────────────────────────── */
@@ -177,72 +138,20 @@ main (int argc, char **argv)
if (!clientAppUri)
goto cleanup;
- /* Security configuration (optional). When certificate, privateKey, and
- trustStore are all omitted the client connects without encryption.
- When any of the three is present, all three are required. */
- const char *clientCertPath = configGet (&clientCfg, "certificate");
- const char *clientKeyPath = configGet (&clientCfg, "privateKey");
- const char *clientTrustStore = configGet (&clientCfg, "trustStore");
- UA_Boolean clientSecure = (clientCertPath != NULL || clientKeyPath != NULL
- || clientTrustStore != NULL);
-
- if (clientSecure && (!clientCertPath || !clientKeyPath || !clientTrustStore))
- {
- UA_LOG_FATAL (UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
- "Incomplete client security config: certificate, "
- "privateKey, and trustStore must all be set, or all "
- "omitted");
- goto cleanup;
- }
-
- UA_MessageSecurityMode securityMode = UA_MESSAGESECURITYMODE_NONE;
- const char *securityPolicyUri
- = "http://opcfoundation.org/UA/SecurityPolicy#None";
-
- if (clientSecure)
- {
- const char *securityModeStr
- = configRequire (&clientCfg, "securityMode", "ServerRegister");
- const char *securityPolicyStr
- = configRequire (&clientCfg, "securityPolicy", "ServerRegister");
- if (!securityModeStr || !securityPolicyStr)
- goto cleanup;
-
- securityMode = parseSecurityMode (securityModeStr);
- if (securityMode == UA_MESSAGESECURITYMODE_INVALID)
- {
- UA_LOG_FATAL (UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
- "Unknown security mode: %s", securityModeStr);
- goto cleanup;
- }
-
- securityPolicyUri = resolveSecurityPolicyUri (securityPolicyStr);
- if (!securityPolicyUri)
- {
- UA_LOG_FATAL (UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
- "Unknown security policy: %s", securityPolicyStr);
- goto cleanup;
- }
-
- if (loadTrustStore (clientTrustStore, &clientTrustPaths,
- &clientTrustSize)
- != 0)
- goto cleanup;
- }
-
- const char *clientUsername = NULL, *clientPassword = NULL;
- UA_Boolean clientCertAuth = false;
- if (parseAuthConfig (&clientCfg, "ServerRegister", NULL, &clientUsername,
- &clientPassword, &clientCertAuth)
+ if (parseSecurityConfig (&clientCfg, "ServerRegister", true, &clientSec)
!= 0)
goto cleanup;
+ AuthConfig clientAuth;
+ if (parseAuthConfig (&clientCfg, "ServerRegister", &clientAuth) != 0)
+ goto cleanup;
+
/* ── Create and configure server ────────────────────────────── */
UA_StatusCode retval;
- server = createServer ((UA_UInt16)port, applicationUri, serverCertPath,
- serverKeyPath, serverTrustPaths, serverTrustSize,
- true, &retval);
+ server
+ = createServer ((UA_UInt16)port, applicationUri,
+ serverSec.certPath ? &serverSec : NULL, true, &retval);
if (!server)
goto cleanup;
@@ -252,23 +161,26 @@ main (int argc, char **argv)
/* Configure access control. UA_ServerConfig_setDefaultWithSecure-
SecurityPolicies sets certificate-only auth by default, so we must
always call UA_AccessControl_default to get the desired policy. */
- if (serverAllowAnonymous)
+ switch (serverAuth.mode)
{
+ case AUTH_ANONYMOUS:
retval = UA_AccessControl_default (serverConfig, true, NULL, 0, NULL);
- }
- else if (serverUsername)
- {
- UA_UsernamePasswordLogin logins[1];
- logins[0].username = UA_STRING ((char *)serverUsername);
- logins[0].password = UA_STRING ((char *)serverPassword);
- retval = UA_AccessControl_default (serverConfig, false, NULL, 1, logins);
- }
- else
- {
+ break;
+ case AUTH_USER:
+ {
+ UA_UsernamePasswordLogin logins[1];
+ logins[0].username = UA_STRING ((char *)serverAuth.user.username);
+ logins[0].password = UA_STRING ((char *)serverAuth.user.password);
+ retval
+ = UA_AccessControl_default (serverConfig, false, NULL, 1, logins);
+ break;
+ }
+ case AUTH_CERT:
/* cert auth — sessionPKI.verifyCertificate is set by createServer
via setDefaultWithSecureSecurityPolicies, so UA_AccessControl_default
will automatically advertise the X509 certificate token policy. */
retval = UA_AccessControl_default (serverConfig, false, NULL, 0, NULL);
+ break;
}
if (retval != UA_STATUSCODE_GOOD)
goto cleanup;
@@ -278,16 +190,9 @@ main (int argc, char **argv)
LdsClientParams ldsParams = {
.appUri = clientAppUri,
- .certPath = clientCertPath,
- .keyPath = clientKeyPath,
- .trustPaths = clientTrustPaths,
- .trustSize = clientTrustSize,
- .securityMode = securityMode,
- .securityPolicyUri = securityPolicyUri,
+ .sec = clientSec,
+ .auth = clientAuth,
.logLevel = logLevel,
- .username = clientUsername,
- .password = clientPassword,
- .certAuth = clientCertAuth,
};
/* Use run_startup + manual event loop (instead of UA_Server_run) so we
@@ -360,8 +265,8 @@ main (int argc, char **argv)
cleanup:
if (server)
UA_Server_delete (server);
- freeTrustStore (clientTrustPaths, clientTrustSize);
- freeTrustStore (serverTrustPaths, serverTrustSize);
+ freeTrustStore (clientSec.trustPaths, clientSec.trustSize);
+ freeTrustStore (serverSec.trustPaths, serverSec.trustSize);
configFree (&clientCfg);
configFree (&serverCfg);
return rc;