usmUserStatus notInService

Hi,

I noticed that the agent++ example static_table allows users in the usmUserTable to be used for gets/sets even if the usmUserStatus has been set to notInService. I had a look through RFC3414 which doesn’t specifically mention how the agent should behave if a user has been set to notInService.

I have tested the behaviour with another SNMP implementation, and with that the agent returned
‘Unknown user name’ when trying to authenticate with a user which has been set to notInService.

Is this a bug in the agent++ implementation?

Thanks in advance,

Holger

Hi Holger,
Which version of SNMP++ and AGENT++ are you using?
Best regards
Frank

Independent of the used versions, in SNMPv3 access security has to be implemented in the View Access Control Model. Using User Based Security Model for implicit access security can have unexpected results.

For example, the sender of a SNMPv3 PDU can decide which security level the GET or SET request has. The USM then processes the messages according to the security level. If it is noAuthNoPriv, neither authentication nor privacy takes places. That means a unauthenticated GET/SET PDU is forwarded to the agent and if the VAM is not in place or not properly defined, the request will be processed!

Therefore always use the VACM to security your agent (do not rely on USM user notInService for example).

Hi Frank,

Thanks for the quick response. I am using the latest version:
agent++ v4.1.2
snmp++ v3.3.11a

Even though authorization is handled by VACM, I still find it surprising that a user which is notInService can authenticate.

Thanks,

Holger

Hi Holger,

when setting the status of a user in the UsmUserTable to notInService, the code in UsmUserTableStatus::set() deletes the localized user from the USM, so after this the localized user no longer exists in snmp++.

Unfortunately the example static_table adds all users to the UsmUserTable using passwords and does not set the addPasswordsToUSM parameter to false (it is omited and default is true). So the USM still has the username and passwords and will create the localized user when the next request arrives.

Kind regards,
Jochen

Hi Jochen,

Thanks for sorting this out. I will change the examples to set the addPasswordToUSM=false as show below:

((UsmUserTable*)usmUserTable)->addNewRow(..., false) 

A SNMP agent should not use passwords for its USM users, localised keys should be used instead!
The SNMP-USER-BASED-SM-MIB does not cover non-localised users. That might cause misunderstandings, and the agent will not contain any secure information in memory, that can be used to exploit other devices.

The two different USM user types (localised vs. non-localised), are the reason why setting a row in the usmUserTable to notInService will not affect the non-localised user in the SNMP++ USM.

Yes, it is surprisingly and that’s why I recommend to always use the VACM to implement access security in order to avoid accidentally created security holes.

Thanks Jochen and Frank,

Your suggested fix works, until the agent gets restarted. If I clone a user the new user gets added as nonVolatile. I can then set the new user to notInService and the agent responds with “Unknown user name” if I try to authenticate with it. After restarting the agent (I have persistent storage enabled) the new user will still be shown as notInService, but now I can successfully authenticate again with the disabled user.

Can you think of any fix that would work after a restart for nonVolatile users ?

Thanks for your help,

Holger

Hi Holger,

That is a bug and will be fixed in the next release as follows:

void UsmUserTable::row_init(MibTableRow* new_row, const Oidx& ind, MibTable*)
{
	initialize_key_change(new_row);
	// add user to USM
	OctetStr engineID, userName, secName, authKey, privKey;
	Oidx authProtocol, privProtocol;
    int rowStatus;

	new_row->get_nth(0)->get_value(engineID);
	new_row->get_nth(1)->get_value(userName);
	new_row->get_nth(2)->get_value(secName);
	new_row->get_nth(4)->get_value(authProtocol);
	new_row->get_nth(5)->get_value(authKey);
	new_row->get_nth(7)->get_value(privProtocol);
	new_row->get_nth(8)->get_value(privKey);
        new_row->get_nth(12)->get_value(rowStatus);
	long int auth = authProtocol.last();
	long int priv = privProtocol.last();
    if (rowStatus == rowActive) {
        usm->add_localized_user(engineID, userName, secName,
                			   auth, authKey, priv, privKey);
    }
}

In the current version the loaded row will be added regardless of its user status to the USM. The fix will add it only when it is active.

Frank

Hi,

I have tried Franks fix for UsmUserTable::row_init and it fixes the reboot issue.

I have found one more issue. I can deactivate a user, but reactivating fails:

# deactivating user SHAAES128
snmpusm -v 3 -l authNoPriv -u SHA -a sha -A SHAUserAuthPassword 127.0.0.1 deactivate SHAAES128
User successfully deactivated.

# re-activating user SHAAES128
snmpusm -v 3 -l authNoPriv -u SHA -a sha -A SHAUserAuthPassword 127.0.0.1 activate SHAAES128
Error in packet.
Reason: inconsistentValue (The set value is illegal or unsupported in some way)
Failed object: SNMP-USER-BASED-SM-MIB::usmUserStatus."...p.ubuntu1804.."."SHAAES128"

# deleting user SHAAES128
snmpusm -v 3 -l authNoPriv -u SHA -a sha -A SHAUserAuthPassword 127.0.0.1 delete SHAAES128
User successfully deleted.

Any ideas how this could be fixed?

Thank you,

Holger

Hi Holger,

I will check that. Seems to be a bug.

Best regards,
Frank

Hi Holger,

I have fixed the bug. There were changes necessary at several locations in v3_mibs.h and v3_mibs.cpp.
This fix will be included in release 4.1.3 which will be released on 2020-02-16T23:00:00Z if all further pending tests are fine.

v3_mib.cpp:

474,475c474,477
< bool UsmUserTable::ready_for_service(Vbx* pvbs, int sz)
< {
---
> bool UsmUserTable::ready(Vbx* pvbs, int sz, MibTableRow* row) {
>     bool ready = MibTable::ready(pvbs, sz, row);
>     if (ready) {
>         if (row->get_row_status()->get() != rowNotInService) {
479c481
<   if (o == "0.0")
---
>             if (o == "0.0") {
481,482c483,487
< 
<   return TRUE;
---
>             }
>             return TRUE;      
>         }
>     }
>     return ready;
606a612,622
> void UsmUserTable::row_deactivated(MibTableRow* row, const Oidx& ind, MibTable*) {
>     LOG_BEGIN(loggerModuleName, DEBUG_LOG | 1);
>     LOG("UsmUserTable: deactivated row with index");
>     LOG(ind.get_printable());
>     LOG_END;
>     Oidx zeroDotZero = "0.0";
>     OctetStr emptyString;
>     row->get_nth(3)->set_value(zeroDotZero);
> }
> 
> 

v3_mib.h:

131c131
<   virtual bool ready_for_service(Vbx* pvbs, int sz);
---
>   virtual bool ready(Vbx*, int, MibTableRow*);
133a134
>   virtual void row_deactivated(MibTableRow*, const Oidx&, MibTable*);

Hi Frank,

Thanks for the patch. I tested it and I can now successfully re-active a user.

Thanks again for all the help,

Holger

Hi,

I think I might have found another issue. I have applied all the patches in this thread including the one from above to fix the issue with re-activating a user, but if I try the same sequence on a user with storageType set to permanent and then try to reactive the user I get the same error.
I have modified the static_table example code to create the SHAAES128 user like this:

MibTableRow* r;
r = uut->addNewRow("SHAAES128", "SHAAES128",
	       SNMP_AUTHPROTOCOL_HMACSHA,
	       SNMP_PRIVPROTOCOL_AES128,
	       "SHAAES128UserAuthPassword",
	       "SHAAES128UserPrivPassword",
	       false);
if(r) uut->set_storage_type(r, storageType_permanent);

Going through the test sequence again:

# deactivating user SHAAES128
snmpusm -v 3 -l authNoPriv -u SHA -a sha -A SHAUserAuthPassword 127.0.0.1 deactivate SHAAES128
User successfully deactivated.

# re-activating user SHAAES128
snmpusm -v 3 -l authNoPriv -u SHA -a sha -A SHAUserAuthPassword 127.0.0.1 activate SHAAES128
Error in packet.
Reason: inconsistentValue (The set value is illegal or unsupported in some way)
Failed object: SNMP-USER-BASED-SM-MIB::usmUserStatus."...p.ubuntu1804.."."SHAAES128"

# deleting user SHAAES128
snmpusm -v 3 -l authNoPriv -u SHA -a sha -A SHAUserAuthPassword 127.0.0.1 delete SHAAES128
User successfully deleted.

Additionally, I don’t think I should be able to delete a permanent user. RFC2579 says:
A row which is permanent(4) can be changed but not deleted.

Thank you,

Holger

To be able to reactive a permanent user you need to change the StorageType::value_ok method to:

bool StorageType::value_ok(const Vbx& vb)
{
	long v;
	if (vb.get_value(v) != SNMP_CLASS_SUCCESS)
	    return FALSE;
	if ((v < 1) || (v > 5)) return FALSE;
	if ((valid()) && (get_state() < storageType_permanent) && 
                (v >=storageType_permanent)) return FALSE;
	if ((valid()) && (get_state() >= storageType_readOnly)) return FALSE;
	return TRUE;
}

In the current version the last comparison uses storageType_permanent instead of storageType_readOnly.

For the fix to protect readonly and permanent rows for modification and deletion respectively, a more complex patch is needed, but that is already implemented and working (using a MibTableVoter).

Thanks Frank,

I can confirm the patch fixes my issue of reactivating permanent users. Looking forward to the new version.

Thank you,

Holger