Hi, we are trying to implement 2FA for Cisco ASA Anyconnect VPN clients. The ASA supports a “secondary password” input, so the dialog asks for a username, a password and another password. The ASA is going to fire off an Access-Request for each of the passwords. The first one with the users password and if successful, a subsequent request, which should contain the one time authentication token. We couldn’t figure out yet how to authenticate the subsequent request against a different authentication module, especially because they both look the same, besides the Request Id. Can anyone help out how to handle the latter different from the first request in an unlang config? Thanks for your help! Stefan rad_recv: Access-Request packet from host 10.30.9.1 port 8272, id=66, length=611 User-Name = "admin" User-Password = “supersecret" NAS-Port = 491520 Called-Station-Id = "1.2.3.4" Calling-Station-Id = "2.3.4.5" NAS-Port-Type = Virtual Tunnel-Client-Endpoint:0 = "2.3.4.5" Cisco-AVPair = "mdm-tlv=device-platform=mac-intel" Cisco-AVPair = "mdm-tlv=device-type=MacBookPro11,1" Cisco-AVPair = "mdm-tlv=device-mac=aa-aa-aa-aa-aa-aa" Cisco-AVPair = "mdm-tlv=device-mac=bb-bb-bb-bb-bb-bb" Cisco-AVPair = "mdm-tlv=device-platform-version=10.11.6" Cisco-AVPair = "mdm-tlv=ac-user-agent=AnyConnect" Cisco-AVPair = "mdm-tlv=device-uid=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" NAS-IP-Address = 10.30.9.1 Cisco-AVPair = "audit-session-id=c0a800000000000000000000" Cisco-AVPair = "ip:source-ip=2.3.4.5" Vendor-3076-Attr-146 = 0x43454e5452414c Vendor-3076-Attr-150 = 0x00000002 Cisco-AVPair = "coa-push=true" rad_recv: Access-Request packet from host 10.30.9.1 port 8272, id=67, length=611 User-Name = "admin" User-Password = “2fa-otp-token" NAS-Port = 491520 Called-Station-Id = "1.2.3.4" Calling-Station-Id = "2.3.4.5" NAS-Port-Type = Virtual Tunnel-Client-Endpoint:0 = "2.3.4.5" Cisco-AVPair = "mdm-tlv=device-platform=mac-intel" Cisco-AVPair = "mdm-tlv=device-type=MacBookPro11,1" Cisco-AVPair = "mdm-tlv=device-mac=aa-aa-aa-aa-aa-aa" Cisco-AVPair = "mdm-tlv=device-mac=bb-bb-bb-bb-bb-bb" Cisco-AVPair = "mdm-tlv=device-platform-version=10.11.6" Cisco-AVPair = "mdm-tlv=ac-user-agent=AnyConnect" Cisco-AVPair = "mdm-tlv=device-uid=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" NAS-IP-Address = 10.30.9.1 Cisco-AVPair = "audit-session-id=c0a800000000000000000000" Cisco-AVPair = "ip:source-ip=2.3.4.5" Vendor-3076-Attr-146 = 0x43454e5452414c Vendor-3076-Attr-150 = 0x00000002 Cisco-AVPair = "coa-push=true"
On Jan 5, 2017, at 5:52 PM, Stefan Schlesinger <sts@ono.at> wrote:
we are trying to implement 2FA for Cisco ASA Anyconnect VPN clients.
The ASA supports a “secondary password” input, so the dialog asks for a username, a password and another password. The ASA is going to fire off an Access-Request for each of the passwords. The first one with the users password and if successful, a subsequent request, which should contain the one time authentication token.
How are the two requests corrected? i.e. how do you know that they're both for the same connection attempt?
We couldn’t figure out yet how to authenticate the subsequent request against a different authentication module, especially because they both look the same, besides the Request Id.
That's a problem then.
Can anyone help out how to handle the latter different from the first request in an unlang config?
If you can't tell them apart, then there's no magic policies which can tell them apart. You'll need to write policies which track the connection attempts, likely keyed by MAC address. You'll need to store the connection attempts in a database, (or rlm_cache), and check them there. In pseudo-code: expire database entries for this mac which are more than 1 minute old if (no database entry for mac) { must be first password... check first password if fail, reject otherwise store MAC / timestamp in database return access-accept } delete database entry for this mac check second password if fail, reject else return access-accept. TBH, this is what Access-Challenge should be used for. RADIUS has supported challenge-response since 1993 or so. It's stupid to re-invent a *worse* system. And not only worse, stupidly worse. It would have been trivial for them to add an attribute let you differentiate the two packets. But no... it has to be done the worst possible way.... Alan DeKok.
Am 05.01.2017 um 23:52 schrieb Stefan Schlesinger:
Hi,
we are trying to implement 2FA for Cisco ASA Anyconnect VPN clients.
The ASA supports a “secondary password” input, so the dialog asks for a username, a password and another password. The ASA is going to fire off an Access-Request for each of the passwords. The first one with the users password and if successful, a subsequent request, which should contain the one time authentication token.
We couldn’t figure out yet how to authenticate the subsequent request against a different authentication module, especially because they both look the same, besides the Request Id.
Can anyone help out how to handle the latter different from the first request in an unlang config?
Do you really want to use the econdary password option? I'd rather use a real 2FA system like privacyIDEA which uses FreeRadius. Michael
On 07 Jan 2017, at 12:08, Muenz, Michael <m.muenz@spam-fetish.org> wrote: Am 05.01.2017 um 23:52 schrieb Stefan Schlesinger: Do you really want to use the econdary password option?
Not necessarily, I will try to find out whether the ASA supports the Access-Challenge pattern as well. Its just one of the ways Duo has implemented this on the ASA side.
I'd rather use a real 2FA system like privacyIDEA which uses FreeRadius.
Well the idea is to use the secondary password option to compare the received password against the TOTP token stored in Keycloak, which doesn’t provide freeradius support yet. Best, Stefan.
On 07 Jan 2017, at 12:58, Stefan Schlesinger <sts@ono.at> wrote:
On 07 Jan 2017, at 12:08, Muenz, Michael <m.muenz@spam-fetish.org> wrote: Am 05.01.2017 um 23:52 schrieb Stefan Schlesinger: Do you really want to use the econdary password option?
Not necessarily, I will try to find out whether the ASA supports the Access-Challenge pattern as well. Its just one of the ways Duo has implemented this on the ASA side.
I was successfully able to test Cisco Anyconnect with Access-Challenge responses. The client will automatically bring up a new dialog where one is able to enter the challenge. The challenge will be send in a second request in the same radius session, again in the User-Password field. I still have one more problem: my authentication backend (Keycloak) requires me to verify username, password and the token at the same time. I was thinking about writing a Perl authenticator for OpenID Connect with OTP tokens, but therefore I’d need to find a way to cache the User-Password field from the initial Access-Request, to verify it together with the provided OTP token from the Access-Challenge response. Any hints? Best, Stefan
On Jan 10, 2017, at 1:15 AM, Stefan Schlesinger <sts@ono.at> wrote:
I was successfully able to test Cisco Anyconnect with Access-Challenge responses. The client will automatically bring up a new dialog where one is able to enter the challenge. The challenge will be send in a second request in the same radius session, again in the User-Password field.
That's better.
I still have one more problem: my authentication backend (Keycloak) requires me to verify username, password and the token at the same time. I was thinking about writing a Perl authenticator for OpenID Connect with OTP tokens, but therefore I’d need to find a way to cache the User-Password field from the initial Access-Request, to verify it together with the provided OTP token from the Access-Challenge response.
In v3.0.12, cache the password in session-state. if (!&session-state:User-Password) { update session-state { User-Password := &request:User-Password } } else { # it's the second time around... } Alan DeKok.
On 10 Jan 2017, at 16:17, Alan DeKok <aland@deployingradius.com> wrote: In v3.0.12, cache the password in session-state.
Is there a specific reason to use 3.0.12? The lastest available pre-build package I found is 3.0.11-1labs-1~jessie.
if (!&session-state:User-Password) { update session-state { User-Password := &request:User-Password } } else { # it's the second time around... }
This doesn’t work for me at the moment. Do I need to enable any other modules for session storage? How does Freeradius match the two requests in a single session. Does this work? This is the initial request: (0) Received Access-Request Id 170 from 10.30.9.1:8272 to 10.30.9.11:1812 length 611 ... (0) if (!&session-state:User-Password) -> TRUE (0) if (!&session-state:User-Password) { (0) update session-state { (0) User-Password := request:User-Password -> ’xxxxx' (0) } # update session-state = noop ... (0) session-state: Saving cached attributes (0) User-Password = "asdf123" (0) Sent Access-Challenge Id 170 from 10.30.9.11:1812 to 10.30.9.1:8272 length 0 (0) Reply-Message = "Please enter the verification code from Google Authenticator." (0) State = 0x6368616c6c656e6765 (0) Finished request Waking up in 4.9 seconds. (0) Cleaning up request packet ID 170 with timestamp +11 Ready to process requests Now the TOTP Token gets entered and I receive the reply: (1) Received Access-Request Id 171 from 10.30.9.1:8272 to 10.30.9.11:1812 length 622 ... (1) session-state: No cached attributes …. (1) if (!&session-state:User-Password) -> TRUE (1) if (!&session-state:User-Password) { (1) update session-state { (1) User-Password := request:User-Password -> ‘11111111' (1) } # update session-state = noop ... (1) Sent Access-Challenge Id 171 from 10.30.9.11:1812 to 10.30.9.1:8272 length 0 (1) Reply-Message = "Please enter the verification code from Google Authenticator." (1) State = 0x6368616c6c656e6765
On Jan 10, 2017, at 4:25 PM, Stefan Schlesinger <sts@ono.at> wrote:
On 10 Jan 2017, at 16:17, Alan DeKok <aland@deployingradius.com> wrote: In v3.0.12, cache the password in session-state.
Is there a specific reason to use 3.0.12? The lastest available pre-build package I found is 3.0.11-1labs-1~jessie.
It's trivial to build your own packages. See the Wiki. And there are reasons to use 3.0.12. Bug fixes, for one.
if (!&session-state:User-Password) { update session-state { User-Password := &request:User-Password } } else { # it's the second time around... }
This doesn’t work for me at the moment. Do I need to enable any other modules for session storage?
No.
How does Freeradius match the two requests in a single session. Does this work?
It uses the State attribute, as per the RFC.
This is the initial request:
(0) Received Access-Request Id 170 from 10.30.9.1:8272 to 10.30.9.11:1812 length 611 ... (0) if (!&session-state:User-Password) -> TRUE (0) if (!&session-state:User-Password) { (0) update session-state { (0) User-Password := request:User-Password -> ’xxxxx' (0) } # update session-state = noop ... (0) session-state: Saving cached attributes (0) User-Password = "asdf123" (0) Sent Access-Challenge Id 170 from 10.30.9.11:1812 to 10.30.9.1:8272 length 0 (0) Reply-Message = "Please enter the verification code from Google Authenticator." (0) State = 0x6368616c6c656e6765
Why is the State attribute being mangled? It is NOT supposed to be "challenge".
(0) Finished request Waking up in 4.9 seconds. (0) Cleaning up request packet ID 170 with timestamp +11 Ready to process requests
Now the TOTP Token gets entered and I receive the reply:
(1) Received Access-Request Id 171 from 10.30.9.1:8272 to 10.30.9.11:1812 length 622 ... (1) session-state: No cached attributes
Because the State attribute has been mangled.
(1) if (!&session-state:User-Password) -> TRUE (1) if (!&session-state:User-Password) { (1) update session-state { (1) User-Password := request:User-Password -> ‘11111111' (1) } # update session-state = noop ... (1) Sent Access-Challenge Id 171 from 10.30.9.11:1812 to 10.30.9.1:8272 length 0 (1) Reply-Message = "Please enter the verification code from Google Authenticator." (1) State = 0x6368616c6c656e6765
Which is (again) "challenge". Don't mangle the State attribute. It is breaking the server. And why the heck would you go mangle the State attribute in the first place? It doesn't make any sense. Alan DeKok.
participants (3)
-
Alan DeKok -
Muenz, Michael -
Stefan Schlesinger