kbr / fritzconnection

Python-Tool to communicate with the AVM Fritz!Box by the TR-064 protocol and the AHA-HTTP-Interface
MIT License
304 stars 59 forks source link

syntax / usage SetSSID, SetSecurityKeys #66

Open tobit6795 opened 3 years ago

tobit6795 commented 3 years ago

sorry i didnt know where to ask

so enabling WLAN like that works for me:

fc.call_action('WLANConfiguration3', 'SetEnable', NewEnable=True)

i wanted to set the SSID / Key, but im still not lucky

fc.call_action('WLANConfiguration3', 'SetSSID', NewSSID='guest-net')

current error say 402 / Argument error

same goes for setting the keys

fc.call_action('WLANConfiguration3', 'SetSecurityKeys', NewPreSharedKey='St4Rk3SP422w0rD')

i got the SSID set with 'SetEnable' instead of SetSSID

fc.call_action('WLANConfiguration3', 'SetEnable', NewSSID='guest-net') which confuses me even more

so i tried this

arguments={'SetSSID': {'NewSSID': 'guest-net'}}
fc.call_action("WLANConfiguration3", 'SetEnable', arguments=arguments)

i got no error like befor, but nothing was changed

pls help

kbr commented 3 years ago

This indeed is a strange behaviour I can reproduce (using a 7590 with 7.20). After creating a new user with the proper rights, it seems to be that the service description information and also the AVM-documentation differ from the implementation.

However at least I was able to change the name of the guest network and activate and deactivate it the same way as you mentioned:

# activate guest network with a new name:
# service_name may differ depending on the device
guest_network_service = 'WLANConfiguration3'

# this action-parameter combination is undocumented, but works:
fc.call_action(guest_network_service, 'SetEnable', NewSSID='example')

# this action works according to the documentation:
fc.call_action(guest_network_service, 'SetEnable', NewEnable=True)

That's something that may get discussed with the vendor.

tobit6795 commented 3 years ago

any suggestions on the actions SetSSID / SetSecurityKeys ?

EDIT i think i understood now, im not allowed to skip the other args like NewWEPKey1-3, NewPresharedKey and need to define them at least empty..

EDIT2 okay my suggestion above works with the pure xml method (api call with curl btw) but not with the python module

solderdot72 commented 3 years ago

I've observed a simlar issue: Enabling/disabling guest-WIFI works fine, changing the password does not work at all. Here's one of the Python code snippets I use:

securityKeys = fc.call_action('WLANConfiguration:3', 'GetSecurityKeys')
pp.pprint(securityKeys)

newKeys={'NewKeyPassphrase':'MeinPasswortIstGut', 'NewPreSharedKey':'XYZ'}
fc.call_action('WLANConfiguration3', 'SetSecurityKeys', arguments=newKeys)

The reponse to this request is always:

fritzconnection.core.exceptions.FritzArgumentError: UPnPError:
errorCode: 402
errorDescription: Invalid Args

I contacted AVM, provided them a Wireshark trace and I got the information that apparently there is a bug inside FritzConnection. The request body FritzConnection sends out looks like this:

<?xml version="1.0" encoding="utf-8"?><s:Envelope s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body>
<u:SetSecurityKeys xmlns:u="urn:dslforum-org:service:WLANConfiguration:3">
<s:NewWEPKey0></s:NewWEPKey0>
<s:NewWEPKey1></s:NewWEPKey1>
<s:NewWEPKey2></s:NewWEPKey2>
<s:NewWEPKey3></s:NewWEPKey3>
<s:NewPreSharedKey></s:NewPreSharedKey>
<s:NewKeyPassphrase>MeinPasswortIstBesser</s:NewKeyPassphrase>
</u:SetSecurityKeys></s:Body></s:Envelope>

The reference code AVM uses, however, creates the following sequence:

<?xml version="1.0" encoding="utf-16"?><s:Envelope s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body>
<u:SetSecurityKeys xmlns:u="urn:dslforum-org:service:WLANConfiguration:3">
<NewWEPKey0></NewWEPKey0>
<NewWEPKey1></NewWEPKey1>
<NewWEPKey2></NewWEPKey2>
<NewWEPKey3></NewWEPKey3>
<NewPreSharedKey></NewPreSharedKey>
<NewKeyPassphrase>MeinPasswort</NewKeyPassphrase>
</u:SetSecurityKeys></s:Body></s:Envelope>

So apparently the "s:" inside the keys and values in the XML should not be there. At least not for this action.

I also got the information that enabling/disabling the WIFI does work only because of a coding flaw in the FritzBox. Now they know about that they are going to fix it in one of the upcoming releases. So it will cease working in the future.

Does this information help in narrowing down the issue and maybe even provide a fix?

kbr commented 3 years ago

Interesting. Debugging the fritzconnection code the request body created from your code snippet

newKeys={'NewKeyPassphrase':'MeinPasswortIstGut', 'NewPreSharedKey':'XYZ'}
fc.call_action('WLANConfiguration3', 'SetSecurityKeys', arguments=newKeys)

looks like this:

<?xml version="1.0" encoding="utf-8"?><s:Envelope s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body>
<u:SetSecurityKeys xmlns:u="urn:dslforum-org:service:WLANConfiguration:3">
<s:NewKeyPassphrase>MeinPasswortIstGut</s:NewKeyPassphrase>
<s:NewPreSharedKey>XYZ</s:NewPreSharedKey>
</u:SetSecurityKeys></s:Body></s:Envelope>

The additional arguments like

<s:NewWEPKey0></s:NewWEPKey0>

are not created (as they are not given as arguments). So I wonder why the wireshark trace reports this.

However, the error code 402 reported from the box can get reproduced, even by adding the NewWEPKey arguments to the request.

But removing the "s:" from the template does also not work and results in another exception:

FritzArgumentStringToShortError: UPnPError: 
errorCode: 801
errorDescription: String Argument too short

Also changing the encoding from utf-8 to utf-16 as given by the AVM reference code (and encoding the payload accordingly of course) returns an

FritzConnectionException: UPnPError: 
errorCode: 502
errorDescription: XML error

regardless whether the "s:" has been part of the template or not.

Keeping the "s:" still works on other services and actions with the documented TR-064 api (at least the services I've used on a FRITZ!Box 7590 with FRITZ!OS 7.20).

If there is a bug in fritzconnection (I'm sure there are bugs, because there is no thing like a bug-free software), this would be great, because then it could get solved. But currently I have no idea.

solderdot72 commented 3 years ago

I need to apologize. The code snippet used for this trace looks as follows:

securityKeys = fc.call_action('WLANConfiguration:3', 'GetSecurityKeys')
securityKeys['NewKeyPassphrase']='MeinPasswortIstBesser'
fc.call_action('WLANConfiguration3',
               'SetSecurityKeys',
               arguments=securityKeys)

So this explains why the WireShark trace contains the

<s:NewWEPKey0></s:NewWEPKey0>

lines: They are there since they are part of the dict which was obtained by the FritzBox earlier and which is modified by the code to change the password.

solderdot72 commented 3 years ago

I did some more trials and found a quick and dirty hack which actually works. I am aware that this is far from being good code, however I would say this is a confirmation that AVM's statement is true: In this case the 's:' is malicious. I did as follows:

In soaper.py I added below line 226 (this is in the method execute(), the following code snippet:

        if 'NewKeyPassphrase' in body:
            print (body)
            print ('replacing......................................')
            body = body.replace('s:NewWEPKey0', 'NewWEPKey0')
            body = body.replace('s:NewWEPKey1', 'NewWEPKey1')
            body = body.replace('s:NewWEPKey2', 'NewWEPKey2')
            body = body.replace('s:NewWEPKey3', 'NewWEPKey3')
            body = body.replace('s:NewPreSharedKey', 'NewPreSharedKey')
            body = body.replace('s:NewKeyPassphrase', 'NewKeyPassphrase')
            print (body)

This removes the 's:' from that very body in that very case. This works fine, the password gets changed.

So my script looks like as follows:

from fritzconnection import FritzConnection
import pprint

pp = pprint.PrettyPrinter(indent=2)
fc=FritzConnection(address='192.168.2.1', user='FritzConnection', password='Dr3QL78ZYG58Nn98GVsx', use_tls=True)

securityKeys = fc.call_action('WLANConfiguration:3', 'GetSecurityKeys')
pp.pprint(securityKeys)

securityKeys['NewKeyPassphrase']='MeinPasswortIstUnfassbarGut'
pp.pprint(securityKeys)
fc.call_action('WLANConfiguration3',
               'SetSecurityKeys',
               arguments=securityKeys)

securityKeys = fc.call_action('WLANConfiguration:3', 'GetSecurityKeys')
pp.pprint(securityKeys)

The following output is generated:

{ 'NewKeyPassphrase': 'MeinPasswortIstPrima',
  'NewPreSharedKey': 'FC5EE08753F83ADA475B75CB65476525B37184349E09E0C178001A6DA02C2DBA',
  'NewWEPKey0': '',
  'NewWEPKey1': '',
  'NewWEPKey2': '',
  'NewWEPKey3': ''}
{ 'NewKeyPassphrase': 'MeinPasswortIstUnfassbarGut',
  'NewPreSharedKey': 'FC5EE08753F83ADA475B75CB65476525B37184349E09E0C178001A6DA02C2DBA',
  'NewWEPKey0': '',
  'NewWEPKey1': '',
  'NewWEPKey2': '',
  'NewWEPKey3': ''}
<s:Body><u:SetSecurityKeys xmlns:u="urn:dslforum-org:service:WLANConfiguration:3"><s:NewWEPKey0></s:NewWEPKey0><s:NewWEPKey1></s:NewWEPKey1><s:NewWEPKey2></s:NewWEPKey2><s:NewWEPKey3></s:NewWEPKey3><s:NewPreSharedKey>FC5EE08753F83ADA475B75CB65476525B37184349E09E0C178001A6DA02C2DBA</s:NewPreSharedKey><s:NewKeyPassphrase>MeinPasswortIstUnfassbarGut</s:NewKeyPassphrase></u:SetSecurityKeys></s:Body>
replacing......................................
<s:Body><u:SetSecurityKeys xmlns:u="urn:dslforum-org:service:WLANConfiguration:3"><NewWEPKey0></NewWEPKey0><NewWEPKey1></NewWEPKey1><NewWEPKey2></NewWEPKey2><NewWEPKey3></NewWEPKey3><NewPreSharedKey>FC5EE08753F83ADA475B75CB65476525B37184349E09E0C178001A6DA02C2DBA</NewPreSharedKey><NewKeyPassphrase>MeinPasswortIstUnfassbarGut</NewKeyPassphrase></u:SetSecurityKeys></s:Body>
{ 'NewKeyPassphrase': 'MeinPasswortIstUnfassbarGut',
  'NewPreSharedKey': '48F93E91801FB6E2EE1374E5B9026CEA92468FC0C9CBD7DE2B3D139DD04B9850',
  'NewWEPKey0': '',
  'NewWEPKey1': '',
  'NewWEPKey2': '',
  'NewWEPKey3': ''}

So to me it looks like AVM support is right and the 's:' should not be there.

kbr commented 3 years ago

Indeed, fixing this a second time works. But I have done nothing different than in the first attempt to change just the argument_template (at least I think so) – so I wonder why it now works ...

The clean way to change this, instead of using replace, is adapting the argument_template around line 180 of soaper.py.

However, for all other action-calls the previous version using the "s:" namespace prefix works fine – and still works. So the AVM parser still seems to accept the prefix – except sometimes not.

As the master branch has already some changes for 1.4.0_dev there will be no 1.3.5 and the fix will be in the upcoming 1.4 version. I should modify the branch structure after the next release.

Furthermore I suppose the issue reported by @tobit6795 may be part of the "flaw" mentioned by AVM.

Thank you for reporting this.

DanielR92 commented 3 years ago

Hello everybody, I also tried to fix the problem for the last two days. I came across this post today. Thanks to @solderdot72, I would have been about to bother with wireshark. This workaround helps me first.