[NETCONF-1115] Handle unencrypted password in login-password for topology node Created: 03/Aug/23  Updated: 07/Feb/24

Status: Confirmed
Project: netconf
Component/s: netconf
Affects Version/s: None
Fix Version/s: 7.0.0

Type: Improvement Priority: Medium
Reporter: Sangwook Ha Assignee: Ivan Hrasko
Resolution: Unresolved Votes: 0
Labels: pt
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Blocks
is blocked by NETCONF-1216 Input length must be multiple of 16 w... Resolved
is blocked by NETCONF-1217 Given final block not properly padded... Resolved
Issue split
split to NETCONF-1186 Enforce base64 encoding for netconf-k... Resolved
Relates
relates to AAA-266 AAAEncryptionService does not inform ... Resolved

 Description   

netconf-node-topology:login-password/password is supposed to keep the encrypted password of a NETCONF device. However, in some cases a password saved in field may work even if it's not properly encrypted because AAAEncryptionService.decrypt may return the original password when decryption fails.

For example, the following request with unencrypted password

PUT /rests/data/network-topology:network-topology/topology=topology-netconf/node=netconf-mdsal

{
    "network-topology:node": [
        {
            "node-id": "netconf-mdsal",
            "netconf-node-topology:concurrent-rpc-limit": 0,
            "netconf-node-topology:schema-cache-directory": "netconf-mdsal",
            "netconf-node-topology:login-password": {
                "username": "admin",
                "password": "adminadmin"
            },
            "netconf-node-topology:default-request-timeout-millis": 1800000,
            "netconf-node-topology:port": 2830,
            "netconf-node-topology:tcp-only": false,
            "netconf-node-topology:host": "127.0.0.1",
            "netconf-node-topology:actor-response-wait-time": 600,
            "netconf-node-topology:keepalive-delay": 600
        }
    ]
}

makes controller attempt to establish a NETCONF session with adminadmin as the password after failing to decrypt the password:

02:03:07.828 ERROR [opendaylight-cluster-data-notification-dispatcher-58] Failed to decrypt encoded data
javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded cipher
	at com.sun.crypto.provider.CipherCore.prepareInputBuffer(CipherCore.java:888) ~[?:?]
	at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:730) ~[?:?]
	at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:436) ~[?:?]
	at javax.crypto.Cipher.doFinal(Cipher.java:2205) ~[?:?]
	at org.opendaylight.aaa.encrypt.impl.AAAEncryptionServiceImpl.decrypt(AAAEncryptionServiceImpl.java:154) ~[?:?]
	at org.opendaylight.netconf.topology.spi.DefaultNetconfClientConfigurationBuilderFactory.getHandlerFromCredentials(DefaultNetconfClientConfigurationBuilderFactory.java:96) ~[bundleFile:?]
	at org.opendaylight.netconf.topology.spi.DefaultNetconfClientConfigurationBuilderFactory.createClientConfigurationBuilder(DefaultNetconfClientConfigurationBuilderFactory.java:68) ~[bundleFile:?]
	at org.opendaylight.netconf.topology.spi.NetconfNodeHandler.<init>(NetconfNodeHandler.java:143) ~[bundleFile:?]
	at org.opendaylight.netconf.topology.spi.AbstractNetconfTopology.setupConnection(AbstractNetconfTopology.java:142) ~[bundleFile:?]
	at org.opendaylight.netconf.topology.spi.AbstractNetconfTopology.lockedEnsureNode(AbstractNetconfTopology.java:108) ~[bundleFile:?]
	at org.opendaylight.netconf.topology.spi.AbstractNetconfTopology.ensureNode(AbstractNetconfTopology.java:96) ~[bundleFile:?]
	at org.opendaylight.netconf.topology.impl.NetconfTopologyImpl.onDataTreeChanged(NetconfTopologyImpl.java:145) ~[?:?]
	at org.opendaylight.mdsal.binding.dom.adapter.BindingDOMDataTreeChangeListenerAdapter.onDataTreeChanged(BindingDOMDataTreeChangeListenerAdapter.java:44) ~[bundleFile:?]
	at org.opendaylight.controller.cluster.datastore.DataTreeChangeListenerActor.dataTreeChanged(DataTreeChangeListenerActor.java:90) ~[bundleFile:?]
	at org.opendaylight.controller.cluster.datastore.DataTreeChangeListenerActor.handleReceive(DataTreeChangeListenerActor.java:45) ~[bundleFile:?]
	at akka.japi.pf.UnitCaseStatement.apply(CaseStatements.scala:24) ~[bundleFile:?]
	at akka.japi.pf.UnitCaseStatement.apply(CaseStatements.scala:20) ~[bundleFile:?]
	at scala.PartialFunction.applyOrElse(PartialFunction.scala:214) ~[bundleFile:?]
	at scala.PartialFunction.applyOrElse$(PartialFunction.scala:213) ~[bundleFile:?]
	at akka.japi.pf.UnitCaseStatement.applyOrElse(CaseStatements.scala:20) ~[bundleFile:?]
	at scala.PartialFunction$OrElse.applyOrElse(PartialFunction.scala:269) ~[bundleFile:?]
	at scala.PartialFunction$OrElse.applyOrElse(PartialFunction.scala:270) ~[bundleFile:?]
	at akka.actor.Actor.aroundReceive(Actor.scala:537) ~[bundleFile:?]
	at akka.actor.Actor.aroundReceive$(Actor.scala:535) ~[bundleFile:?]
	at akka.actor.AbstractActor.aroundReceive(AbstractActor.scala:220) ~[bundleFile:?]
	at akka.actor.ActorCell.receiveMessage(ActorCell.scala:579) ~[bundleFile:?]
	at akka.actor.ActorCell.invoke(ActorCell.scala:547) ~[bundleFile:?]
	at akka.dispatch.Mailbox.processMailbox(Mailbox.scala:270) ~[bundleFile:?]
	at akka.dispatch.Mailbox.run(Mailbox.scala:231) ~[bundleFile:?]
	at akka.dispatch.Mailbox.exec(Mailbox.scala:243) ~[bundleFile:?]
	at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:373) ~[?:?]
	at java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1182) ~[?:?]
	at java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1655) ~[?:?]
	at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1622) ~[?:?]
	at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:165) ~[?:?]

02:03:07.855 WARN [sshd-NetconfSshClient[2914912]-nio2-thread-3] Server at /127.0.0.1:2830 presented unverified EC key: SHA256:3kA4ClemSJjEhFPu1EdCQnWku+XYuWQi33bFFRK90G8

In this case the connection failed because the password is incorrect. But there are many cases where the correct unencrypted password works.

This behavior encourages keeping unencrypted password in the configuration because it may not discovered or it may be simply ignored because it works. Also, this may cause confusion since some may work but some may not.

So the device should not be activated when decryption fails to mitigate the issue.



 Comments   
Comment by Ruslan Kashapov [ 29/Aug/23 ]

Cause:

  • According to odl-netconf-device model (used in netconf-node-topology-node) login-password container expected to contain encrypted password value
  • Encrypted value handling is served by aaa-encrypt-service which provides both encryption for persistence and decryption for usage in ssh connection
  • Due to aaa-encrypt-service is an OSGi component with own configuration having no API exposed to end user, the password encryption is performed dynamically on input processing, encrypted value is not expected to be provided by user
  • Dynamic password value encryption is implemented for netconf-node-topology:create-device RPC processing, but not for direct data insert/update
  • Once node data is updated in configuration datastore the device connection is triggered with assumption password value is properly encrypted

Non-encrypted password value as a case of incorrect configuration to be handled via NETCONF-1114. The expected resolution is no connection to device performed if configuration processing fails with immediate failure status to be set for device.

Delegating password value encryption to user requires at least encryption service API exposed so user would have the ability to encrypt the password value by himself with assurance this value will be properly decrypted (because same service will use same parameters). However this approach requires shared component update, which complicates and delays solution delivery.

The suggested approach is to add dynamic password encryption to direct data update via dedicated DataTreeChangeListener implementation:

  • Scope is configuration datastore
  • Monitor nodes having encrypted password values
  • if encrypted password value added/changed check if the aaa-encrypt-service can perform the value decryption
  • if decryption fails assume the value is not encrypted, then encrypt and override it
Comment by Ivan Hrasko [ 17/Oct/23 ]

We are going from different approach:

Enforce base64 encoding for odl-netconf-device model for of all leafs that are claiming its their type by YANG string type pattern. Analyze RPCs which can be used to create such data, realize that we want to allow user to use plain strings in RPC payloads and adapt payloads YANG definition accordingly.

If needed to modify odl-netconf-device or netconf-node-topology create new revisions in that case.

Comment by Sangwook Ha [ 19/Oct/23 ]

Enforcing base64 encoding will prevent many invalid password values but it's still possible to pass the pattern checking without being actually base64 encoded.
Since AAAEncryptionServiceImpl.decrypt() returns original value when decryption fails and there is no checking of decryption failure in netconf, many plaintext passwords can be put into the field and successfully used as long as the pattern matches the base64 encode format - e.g. any password composed of alphanumeric characters whose length is multiples of 4.

It seems problematic that the decrypt function does not have clear API contract that can signal decryption failure, not even documentation, when it's a public API used by other downstream projects - and also inconsistent in that it catches decryption errors but not that of base64 decoding.

Comment by Peter Suna [ 24/Oct/23 ]

I will investigate the potential consequences of AAAEncryptionServiceImpl throwing an exception in case of encryption or decryption failure. IMHO, the primary concept is to delegate the validation of password values to the data store, relieving upper layers of this responsibility.

Comment by Ivan Hrasko [ 05/Jan/24 ]

The issue is blocked by NETCONF-1216 and NETCONF-1217. The change type to binary does not solve those as the only way to reliably use encryption service is via RPC. Needs more investigation.

Generated at Wed Feb 07 20:16:43 UTC 2024 using Jira 8.20.10#820010-sha1:ace47f9899e9ee25d7157d59aa17ab06aee30d3d.