Skip to content

Instantly share code, notes, and snippets.

@zouppen
Last active March 16, 2022 21:52
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zouppen/31ebf219e7cfe481980744c1d3fbc32f to your computer and use it in GitHub Desktop.
Save zouppen/31ebf219e7cfe481980744c1d3fbc32f to your computer and use it in GitHub Desktop.
Listen to DTMF in Asterisk and pass code to SSH for validation

Asterisk DTMF door opener

This is a script which listens to DTMF tones and collects 6-digit PIN code and passes it to given SSH server for processing. The server is also responsible of doing the action if it's correct. So, the correct codes are never stored on the PBX but on a remote system accessible via SSH key pair. The PIN code is passed as an argument to the command.

Why this and not just a caller ID check?

Caller IDs are so easy to fake, not only by phone phreaks but anyone with a plain web browser and some cryptocurrencies when you know where to go. So you should NEVER use caller phone number as an identification method. A better option is to do callback, meaning you would first check if the phone number is on allow list and then call them back. But that costs money which we wanted to save.

Also, when you have a PIN code, you can save time when you can add the phone number with the PIN code to your contacts and call there directly, as shown next.

Calling directly from contacts

Our PBX is at +358931576900 and the extension for the door bot is 7006 and then comes the PIN code. You need to add about 3 second pause between the extension (7006) and the PIN code to let the PBX to forward your call to the PIN validator. That's somewhat phone dependant how to do it.

In telephony terms this thing is called automatic attendant.

Android and iOS

Add the following to your contacts: +358931576900,7006,,123456 where 123456 is your PIN code.

In dialing screen the comma button is a bit hidden, but is in a submenu. In Android you'd just press the three dots on the left of the dialing screen and similar in iOS, too.

Old Nokias

If you have those old indestructible phones still around, you can use the following format:+358931576900p7006pp123456 where 123456 is your PIN code.

Installing the script

Copy the script to /var/lib/asterisk/agi-bin/. Then add routing, by adding the following to /etc/asterisk/extensions_custom.conf:

[hacklab_door]
exten => s,1,AGI(agi://127.0.0.1/hacklab_door)

If you are using FreePBX, then add hacklab_door,s,1 to Custom destinations. Then add an extension to it from Misc applications menu or add it to IVR menu, or both, depending of your preference.

Code validator component

We have personal PIN code for each person and we log all the visits. However, our implementation is so specific to our hackerspace so there is no point in sharing it. You can do it anyway you want, such as a small application as SSH forced command which validates the codes from database or a text file and returns exit code 0 when the code is correct (and the door is opened) or any other code in case of an invalid code.

Hammering detection

This allows only three tries to make hammering more difficult. If you do it, you'd just fill the call log with failed attempts and you'd just get banned before you'd succeed. If it's still a problem, it's possible to add some caller ID validation, too, to make random hammering a bit more difficult.

The only reason I put the phone number in here is the fact that you could test it if you want. Just do not hammer. :-) Please submit me a patch if you find a security hole or bugs.

#!/usr/bin/env php
<?php
/*
* Hacklab Jyväskylä door bot, by Joel Lehtonen, zouppen@iki.fi.
*/
$restrict_mods = true;
include '/etc/freepbx.conf';
set_time_limit(0);
error_reporting(0);
require_once "phpagi.php";
$AGI = new AGI();
$AGI->answer();
$retries = 3;
$voice = 'en_GB/custom/hacklabjkl-anna-ovikoodi';
for ($i=0; $i<$retries; $i++) {
$ans = $AGI->get_data($voice, 10000, 6);
$safe_code = escapeshellarg($ans['result']);
system("ssh -Tn palvelin -- $safe_code >/dev/null 2>&1", $exitval);
if($exitval === 0) {
$AGI->stream_file('en_GB/custom/hacklabjkl-ovi-on-auki');
$AGI->verbose("Hacklab door code OK, try $i of $retries");
break;
}
$voice = 'en_GB/custom/hacklabjkl-koodi-vaarin';
$AGI->verbose("Hacklab door code failed, try $i of $retries");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment