[Probably Solved]Asterisk Dialplan AGI Script Not Executing (Possible Asterisk Permissions issue?)

I have an unusual situation with my Asterisk.

Firstly the machine is a FreePBX 14, running Asterisk 15.4.0

I have a dialplan which takes card details using an IVR, i.e Enter Card Number followed by the Hash key.

This then puts together a whole string to execute a separate Perl Script that will charge the customers card with the requested amount.

If i run the perl script from the CLI, the script executes fine and charges the card.

If I go through the dialplan, providing the relevant card details, when it gets to the end to execute the script, it all appears to work, but nothing happens in terms of charging the card.

In an effort to see the AGI script running and seeing what’s going wrong, I run asterisk as ‘asterisk -vvvvvc’ as root, and do the same again, the payment goes through, and works completely fine.

This leads me to believe that when running asterisk as (asterisk -vvvc) it runs with elevated permissions allowing the script to run properly.

Any ideas as to how i can have this working normally or what permissions I need to fix.

The script is set to 0777, so should be executable by everything, and I’ve also set the script to be owned by asterisk AND root, and that made no difference.

Here is the command I am using in the dialplan to invoke the script.

exten=>50000,n,AGI(MakePayment.agi,${CardVar})

Although all that does is pass it through to the perl script.

As noted previously, I do not expect there to be an issue with the dialplan nor the script but rather some issue between the two interacting with each other.

I hope you are not handling any data covered by PCI DSS, e.g. CVV, as what you describe, and, in particular, setting scripts world writeable, would never be considered acceptable. Even without the world writeable scripts, it seems likely you will be logging things, which may include CVV.

Generally, though, you seem to be working in a FreePBX environment, and using FreePBX terminology, so you need to ask the FreePBX users on https://community.freepbx.org/

For further support on the current forum, we would need the contents of your scripts and all the dialplan that is executed for your current call. If there are any FreePBX AGI scripts involved, that will probably frustrate any help from the current forum.

Although I don’t think Asterisk does this, some programs will actually refuse to run external executables if the executable, or any of its ancestors directories is world writeable, or even writeable by anyone other than than root or the current user.

The two most common reasons for scripts failing to run are environment variables, particularly PATH, not having appropriate content, and SELinux violations.

David, thanks for the reply, It actually does handle CVV and all card details, however purely in a test environment and with test card details, it’s more of a proof of concept for both the billing service and the possibility of executing scripts within the dialplan, which I have done before, but not much.

I understand the risks at which this would pose in a production environment and would not deploy it in this way. We will be looking into using a more secure way of handling card details over the phone.

While it is technically a FreePBX machine, none of this dialplan or script utilises any of that, which is why I brought it over to this forum rather than the FreePBX one, please let me know if you believe it belongs over there instead though.

As for the code, please forgive it, I am not a coder, more of a hobbyist!

Dialplan below…

exten => 50000,1,Answer(500)
exten => 50000,n,Set(TimeNow=${EPOCH})
exten => 50000,n,Set(TransID=Trans${TimeNow})
exten => 50000,n(Start),Read(MyVar,/var/lib/asterisk/sounds/en/Recording1,1,,3)
exten => 50000,n,GotoIf($["${MyVar}" = "1"]?Pressed1)
exten => 50000,n,GotoIf($["${MyVar}" = "2"]?Pressed2)
exten => 50000,n,GotoIf($["${MyVar}" = "3"]?Pressed3)
exten => 50000,n,GotoIf($["${MyVar}" = "4"]?Pressed4)
exten => 50000,n,GotoIf($["${MyVar}" = "5"]?Pressed5)
exten => 50000,n,GotoIf($["${MyVar}" != "1"]?NoValid)

exten => 50000,n(Pressed1),Noop(Pressed 1!)
exten => 50000,n,Set(Value=10)
exten => 50000,n,Noop(${Value})
exten => 50000,n,Goto(CardNumber)
exten => 50000,n,Hangup()

exten => 50000,n(Pressed2),Noop(Pressed 2!)
exten => 50000,n,Set(Value=20)
exten => 50000,n,Noop(${Value})
exten => 50000,n,Goto(CardNumber)
exten => 50000,n,Hangup()

exten => 50000,n(Pressed3),Noop(Pressed 3!)
exten => 50000,n,Set(Value=30)
exten => 50000,n,Noop(${Value})
exten => 50000,n,Goto(CardNumber)
exten => 50000,n,Hangup()

exten => 50000,n(Pressed4),Noop(Pressed 4!)
exten => 50000,n,Set(Value=40)
exten => 50000,n,Noop(${Value})
exten => 50000,n,Goto(CardNumber)
exten => 50000,n,Hangup()

exten => 50000,n(Pressed5),Noop(Pressed 5!)
exten => 50000,n,Set(Value=50)
exten => 50000,n,Noop(${Value})
exten => 50000,n,Goto(CardNumber)
exten => 50000,n,Hangup()

#Didnt select any valid option from the IVR, loops to start after telling them its invalid.
exten => 50000,n(NoValid),Noop(Pressed Invalid Number)
exten => 50000,n,Playback(/var/lib/asterisk/sounds/en/Recording5)
exten => 50000,n,Goto(Start)
exten => 50000,n,Hangup()

#This part asks for card number
exten => 50000,n(CardNumber),Noop(Getting Card Number)
exten => 50000,n,Read(CardVar,/var/lib/asterisk/sounds/en/Recording2,17)
exten => 50000,n,Noop(${CardVar})

#This part checks to see if its 16 digits long.
exten => 50000,n,Set(CardLength=${LEN(${CardVar})})
exten => 50000,n,GotoIf($["${CardLength}" = "16"]?ExpiryDate:CardLengthWrong)

#Its not 16 digits long, so it must be wrong, and asks them to do it again(loops here)
exten => 50000,n(CardLengthWrong),Noop(Card Length Wrong, length is ${CardLength})
exten => 50000,n,Goto(CardNumber)
exten => 50000,n,Hangup()

#This part asks for Expiry Date
exten => 50000,n(ExpiryDate),Noop(Getting Expiry Date)
exten => 50000,n,Read(ExpiryVar,/var/lib/asterisk/sounds/en/Recording3,7)
exten => 50000,n,Noop(${ExpiryVar})

#This part checks the length to see if they entered a 4 digit (1219) expiry date or a 6 digit (122019) expiry date, we need it in 6 digit form.
exten => 50000,n,Set(ExpiryLength=${LEN(${ExpiryVar})})
exten => 50000,n,GotoIf($["${ExpiryLength}" = "6"]?ExpDate:wronglengthexpiry)

exten => 50000,n(wronglengthexpiry),Noop(Not 6 digit, is it 4?)
exten => 50000,n,GotoIf($["${ExpiryLength}" = "4"]?fourdigitexpiry:InvalidExpiry)

#Here we have detected the expiry is only 4 digits long, we rip the string apart and add the 20 into the middle.
exten => 50000,n(fourdigitexpiry),Noop(Detected a 4 digit expiry, we will add the 20 in for compatibility)
exten => 50000,n,Set(ExpYear=${ExpiryVar:-2})
exten => 50000,n,Noop(Expiry Year is: ${ExpYear})
exten => 50000,n,Set(ExpMonth=${ExpiryVar:0:2})
exten => 50000,n,Noop(Expiry Month is: ${ExpMonth})
exten => 50000,n,Set(ExpiryVar=${ExpMonth}20${ExpYear})
exten => 50000,n,Noop(Fixed Expiry Date is:${ExpiryVar})

#Time to check if this card is in date.
exten => 50000,n(ExpDate),set(CallYear=${STRFTIME(${EPOCH},GMT,%G)})
exten => 50000,n,Noop(Current Year is ${CallYear})
exten => 50000,n,Set(ExpYear=${ExpiryVar:-4})
exten => 50000,n,Noop(Expiry Year is: ${ExpYear})
exten => 50000,n,GotoIf($["${CallYear}" < "${ExpYear}"]?SecurityCode:ExpMonth)

#Check for out of date Month
#First do we need to check, it would have only got here is its either the same year as now, if it is, we need to check month, if it isnt, something went wrong and we'll start them again.
exten => 50000,n(ExpMonth),set(CallMonth=${STRFTIME(${EPOCH},GMT,%m)})
exten => 50000,n,Noop(Current Month is ${CallMonth})
exten => 50000,n,Set(ExpMonth=${ExpiryVar:0:2})
exten => 50000,n,Noop(Expiry Month is: ${ExpMonth})
exten => 50000,n,GotoIf($["${CallYear}" = "${ExpYear}"]?continue:OutOfDate)
exten => 50000,n,Noop(Expires this year, lets check month)
exten => 50000,n,GotoIf($["${CallMonth}" <= "${ExpMonth}"]?SecurityCode:OutOfDate)
exten => 50000,n,Hangup()

#Card is out of date
exten => 50000,n(OutOfDate),Noop(Card is out of date, Current year is ${CallYear} and card year is ${ExpYear} - Current Month is ${CallMonth} and Card Month is ${ExpMonth})
exten => 50000,n,Playback(/var/lib/asterisk/sounds/en/Recording6)
exten => 50000,n,Goto(ExpiryDate)

#This part is used if expiry was invalid length, not 4 or 6.
exten => 50000,n(InvalidExpiry),Noop(Expiry Length is ${ExpiryLength} this is not valid and not handled, we will ask them to type again.)
exten => 50000,n,Playback(/var/lib/asterisk/sounds/en/Recording6)
exten => 50000,n,Goto(ExpiryDate)

#This part asks for security code
exten => 50000,n(SecurityCode),Noop(Getting Security Code)
exten => 50000,n,Read(SecurityVar,/var/lib/asterisk/sounds/en/Recording4,5)
exten => 50000,n,Noop(${SecurityVar})
exten => 50000,n,Set(ExpMonth=${ExpiryVar:0:2})
exten => 50000,n,Noop(Expiry Month is: ${ExpMonth})
exten => 50000,n,Noop(We are charging ${Value} in pounds to card number: ${CardVar} with expiry Date: ${ExpMonth} ${ExpYear} and Security Code: ${SecurityVar})
exten => 50000,n,Noop(We are going to try and execute my script now!)
exten => 50000,n,AGI(MakePayment.agi,${CardVar},${ExpMonth},${ExpYear},${SecurityVar},${Value},${TransID})
exten => 50000,n,Wait(5)
exten => 50000,n,Set(TestVar123=${FILE(/tmp/log.txt)})
exten => 50000,n,Noop(${TestVar123})
exten => 50000,n,Hangup()

Script below…

#!/usr/bin/perl
use Asterisk::AGI;

# the AGI object
my $agi = new Asterisk::AGI;
#Establishing Variables
#./MakePayment.pl 4242424242424242 12 2019 545 100 -this will make a payment for £100 for that card number.
$CNum = $ARGV[0];
$CExpM = $ARGV[1];
$CExpY = $ARGV[2];
$CCsv = $ARGV[3];
$Value = $ARGV[4];
$TransID = $ARGV[5];
$ResultCode = 999;

#Temp value for testing
#$TransID = Trans1234;

#Write to log for testing


#Getting the Token for our card...
$output = `stripe tokens create card -k ############ --number $CNum --exp-month $CExpM --exp-year $CExpY --cvc $CCsv --name`;
#print $output;

$testforfail = substr $output, 0, 1;
if($testforfail eq '{')
{
#Test for fail has returned { - this means successful token creation
#print $output;
$TokenCreated = "True";
} else 
{
$TokenFailure = "True";
}

if($TokenCreated ="True")
{
$token = substr $output, 19, 28;
#print $token;
#print "\n";
$tokenready = "True";
#Retrieve Token
}

if($tokenready = "True")
{
#Time to Create Payment
$paymentoutput = `stripe charge create -k ########## --currency GBP --token $token --amount $Value --description=########`;
#print $paymentoutput
$PaymentDone = "True";
} else {
#print "Failed";
}

if($PaymentDone = "True")
{
#Lets check to see if its successful
if (index($paymentoutput, status               => "succeeded") != -1) {
#It was successful
$ResultCode = "1";
#print "Payment Successful";
} else {
#Payment Not Successful - TODO
}
}

if($ResultCode = "1"){
#Payment Successful, tell phone system

my $filename = '/tmp/log.txt';
open(my $fh, '>>', $filename) or die "Could not open file '$filename' $!";
print $fh "$TransID#$ResultCode\n";
close $fh;
}

Some part of the script are not complete, mostly the result code section, however, the script will still run at command line correctly, and as mentioned originally, will also run in the ‘asterisk vc’ mode.

The first thing I would try is using the absolute path name for stripe.

Hi David,

Tried that, and it made no change :frowning:

The thing I don’t understand is how it works completely fine when asterisk is running as ‘asterisk -vvc’ instead of a normal system boot into asterisk.

I initially thought that, as root, me running asterisk would then run asterisk as root, and maybe asterisk needed to be rooted to run the script properly.

I googled this, and according to a few things I found, Asterisk does run as root by default.

With c, it would be run with a controlling terminal, even though I think AGI intercepts standard input and standard output. Maybe stripe refuses to run in a detached process. As a variation on that, it is possible that stripe insists on standard error being a terminal (and existing).

David,

That would actually make complete sense, the stripe command I’m running is actually a CLI program and is most likely just supposed to be used in a normal terminal and not used the way I have.

I didn’t even think of this and that this may be an issue, but you’ve helped so much.

Thanks for your time and effort, you are a godsend.