|
@@ -246,6 +246,64 @@ checkSignature(const UA_Server *server, const UA_SecureChannel *channel,
|
|
|
return retval;
|
|
|
}
|
|
|
|
|
|
+#ifdef UA_ENABLE_ENCRYPTION
|
|
|
+static UA_StatusCode
|
|
|
+decryptPassword(UA_SecurityPolicy *securityPolicy, void *tempChannelContext,
|
|
|
+ const UA_ByteString *serverNonce, UA_UserNameIdentityToken *userToken) {
|
|
|
+ UA_SecurityPolicyEncryptionAlgorithm *asymEnc =
|
|
|
+ &securityPolicy->asymmetricModule.cryptoModule.encryptionAlgorithm;
|
|
|
+ if(!UA_String_equal(&userToken->encryptionAlgorithm, &asymEnc->uri))
|
|
|
+ return UA_STATUSCODE_BADIDENTITYTOKENINVALID;
|
|
|
+
|
|
|
+ UA_UInt32 tokenSecretLength;
|
|
|
+ UA_ByteString decryptedTokenSecret, tokenServerNonce;
|
|
|
+ if(UA_ByteString_copy(&userToken->password, &decryptedTokenSecret) != UA_STATUSCODE_GOOD)
|
|
|
+ return UA_STATUSCODE_BADIDENTITYTOKENINVALID;
|
|
|
+
|
|
|
+ UA_StatusCode retval = UA_STATUSCODE_BADIDENTITYTOKENINVALID;
|
|
|
+ if(asymEnc->decrypt(securityPolicy, tempChannelContext,
|
|
|
+ &decryptedTokenSecret) != UA_STATUSCODE_GOOD)
|
|
|
+ goto cleanup;
|
|
|
+
|
|
|
+ memcpy(&tokenSecretLength, decryptedTokenSecret.data, sizeof(UA_UInt32));
|
|
|
+
|
|
|
+
|
|
|
+ * Secret Format and the length field must indicate enough data to include
|
|
|
+ * the server nonce. */
|
|
|
+ if(decryptedTokenSecret.length < sizeof(UA_UInt32) + serverNonce->length ||
|
|
|
+ decryptedTokenSecret.length < sizeof(UA_UInt32) + tokenSecretLength ||
|
|
|
+ tokenSecretLength < serverNonce->length)
|
|
|
+ goto cleanup;
|
|
|
+
|
|
|
+
|
|
|
+ * zeroes according to the 1.04.1 specification errata, chapter 3. */
|
|
|
+ for(size_t i = sizeof(UA_UInt32) + tokenSecretLength; i < decryptedTokenSecret.length; i++) {
|
|
|
+ if(decryptedTokenSecret.data[i] != 0)
|
|
|
+ goto cleanup;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ * chapter 3. */
|
|
|
+ tokenServerNonce.length = serverNonce->length;
|
|
|
+ tokenServerNonce.data = &decryptedTokenSecret.data[sizeof(UA_UInt32) + tokenSecretLength - serverNonce->length];
|
|
|
+ if(!UA_ByteString_equal(serverNonce, &tokenServerNonce))
|
|
|
+ goto cleanup;
|
|
|
+
|
|
|
+
|
|
|
+ * decrypted password. The encryptionAlgorithm and policyId fields are left
|
|
|
+ * in the UserToken as an indication for the AccessControl plugin that
|
|
|
+ * evaluates the decrypted content. */
|
|
|
+ memcpy(userToken->password.data, &decryptedTokenSecret.data[sizeof(UA_UInt32)],
|
|
|
+ tokenSecretLength - serverNonce->length);
|
|
|
+ userToken->password.length = tokenSecretLength - serverNonce->length;
|
|
|
+ retval = UA_STATUSCODE_GOOD;
|
|
|
+
|
|
|
+ cleanup:
|
|
|
+ UA_ByteString_deleteMembers(&decryptedTokenSecret);
|
|
|
+ return retval;
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
|
|
|
*
|
|
|
* Part 4, §5.6.3: When the ActivateSession Service is called for the first time
|
|
@@ -334,6 +392,80 @@ Service_ActivateSession(UA_Server *server, UA_SecureChannel *channel,
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+#ifdef UA_ENABLE_ENCRYPTION
|
|
|
+
|
|
|
+ if((request->userIdentityToken.encoding == UA_EXTENSIONOBJECT_DECODED) &&
|
|
|
+ (request->userIdentityToken.content.decoded.type == &UA_TYPES[UA_TYPES_USERNAMEIDENTITYTOKEN])) {
|
|
|
+ UA_UserNameIdentityToken *userToken = (UA_UserNameIdentityToken *)
|
|
|
+ request->userIdentityToken.content.decoded.data;
|
|
|
+
|
|
|
+
|
|
|
+ UA_Byte tokenIndex = 0;
|
|
|
+ for(; tokenIndex < ed->userIdentityTokensSize; tokenIndex++) {
|
|
|
+ if(ed->userIdentityTokens[tokenIndex].tokenType != UA_USERTOKENTYPE_USERNAME)
|
|
|
+ continue;
|
|
|
+ if(UA_String_equal(&userToken->policyId, &ed->userIdentityTokens[tokenIndex].policyId))
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if(tokenIndex == ed->userIdentityTokensSize) {
|
|
|
+ response->responseHeader.serviceResult = UA_STATUSCODE_BADIDENTITYTOKENINVALID;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ * security policy the security policy of the secure channel is used. */
|
|
|
+ UA_SecurityPolicy* securityPolicy;
|
|
|
+ if(ed->userIdentityTokens[tokenIndex].securityPolicyUri.data == NULL)
|
|
|
+ securityPolicy = UA_SecurityPolicy_getSecurityPolicyByUri(server, &ed->securityPolicyUri);
|
|
|
+ else
|
|
|
+ securityPolicy = UA_SecurityPolicy_getSecurityPolicyByUri(server, &ed->userIdentityTokens[tokenIndex].securityPolicyUri);
|
|
|
+ if(!securityPolicy) {
|
|
|
+ response->responseHeader.serviceResult = UA_STATUSCODE_BADINTERNALERROR;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ if(!UA_String_equal(&securityPolicy->policyUri, &UA_SECURITY_POLICY_NONE_URI)) {
|
|
|
+
|
|
|
+ * used for the password from the SecureChannel */
|
|
|
+ void *tempChannelContext = channel->channelContext;
|
|
|
+ if(securityPolicy != channel->securityPolicy) {
|
|
|
+
|
|
|
+ * channel context. Because the client does not provide one in a
|
|
|
+ * #None SecureChannel. We should not need a ChannelContext at all
|
|
|
+ * for asymmetric decryption where the remote certificate is not
|
|
|
+ * used. */
|
|
|
+ response->responseHeader.serviceResult =
|
|
|
+ securityPolicy->channelModule.newContext(securityPolicy,
|
|
|
+ &securityPolicy->localCertificate,
|
|
|
+ &tempChannelContext);
|
|
|
+ if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) {
|
|
|
+ UA_LOG_WARNING_SESSION(&server->config.logger, session, "ActivateSession: "
|
|
|
+ "Failed to create a context for the SecurityPolicy %.*s",
|
|
|
+ (int)securityPolicy->policyUri.length,
|
|
|
+ securityPolicy->policyUri.data);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ response->responseHeader.serviceResult =
|
|
|
+ decryptPassword(securityPolicy, tempChannelContext, &session->serverNonce, userToken);
|
|
|
+
|
|
|
+
|
|
|
+ if(securityPolicy != channel->securityPolicy)
|
|
|
+ securityPolicy->channelModule.deleteContext(tempChannelContext);
|
|
|
+ }
|
|
|
+
|
|
|
+ if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) {
|
|
|
+ UA_LOG_INFO_SESSION(&server->config.logger, session, "ActivateSession: "
|
|
|
+ "Failed to decrypt the password with the status code %s",
|
|
|
+ UA_StatusCode_name(response->responseHeader.serviceResult));
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+#endif
|
|
|
+
|
|
|
|
|
|
response->responseHeader.serviceResult =
|
|
|
server->config.accessControl.activateSession(server, &server->config.accessControl,
|