Issue with shell script called from PHP - php

TLDR;
I have a shell script which works fine when run from the command line, but not if called from within a PHP script (accessed via web).
In both cases, the calling user is www-data.
The line failing is this:
openssl genrsa -des3 -out certs/$PCODE.key -passout env:PASSPHRASE 2048
Why is this happening? How can I debug it?
Full story
I have the following script which is a slightly modified version of this gist for generating self-signed SSL certs.
When I run it from the terminal as www-data it works ok and generates the key files, the CSR and the SSL cert file. But when I call the script from within a PHP script, it outputs an error and no files are generated. What is causing the failure? How can I debug this?
From terminal:
me#machine$ sudo su www-data
www-data#machine$ ./gencert.sh acme
www-data will generate an SSL cert for acme.dev
Command after line 32 executed oK
Passphrase expoted as I7gOnWxWd0hOk38Zu ... FbxL3K3Rzlv
Generating RSA private key, 2048 bit long modulus
..............................................+++
.................+++
e is 65537 (0x10001)
Command after line 49 executed oK
Command after line 54 executed oK
Command after line 65 executed oK
writing RSA key
Command after line 69 executed oK
Signature ok
subject=/C=IR/ST=Alborz/.../emailAddress=noreply#acme.dev
Getting Private key
Command after line 74 executed oK
Resulting files:
certs/acme.key.org
certs/acme.key
certs/acme.csr
certs/acme.crt
From PHP:
$r = `/var/www/testbench/pm/shell/gencert.sh acme`;
echo $r;
No files are generated and the output is this:
www-data will generate an SSL cert for acme.dev
Command after line 32 executed oK
Passphrase expoted as 1Fd1seZoe2XF ... oSmQFJdVpdwOeTo2CK5VjLxp
Error. Return value = 1 after line 49
The line returning 1 is this:
openssl genrsa -des3 -out certs/$PCODE.key -passout env:PASSPHRASE 2048
Here is the modified shell script:
#!/bin/bash
# Bash shell script for generating self-signed certs. Run this in a folder, as it
# generates a few files. Large portions of this script were taken from the
# following artcile:
#
# http://usrportage.de/archives/919-Batch-generating-SSL-certificates.html
# https://deliciousbrains.com/ssl-certificate-authority-for-local-https-development/
# Additional alterations by: Brad Landers
# Date: 2012-01-27
# Script accepts a single argument, the fqdn for the cert
PCODE="$1"
if [ -z "$PCODE" ]; then
echo "Usage: $(basename $0) <PCODE>"
exit 11
fi
THE_USER="$(whoami)"
echo "$THE_USER will generate an SSL cert for $PCODE.dev"
fail_if_error() {
[ $1 != 0 ] && {
echo -n "Error. Return value = $1 after line $LASTLINE"
unset PASSPHRASE
exit 10
}
echo "Command after line $LASTLINE executed oK"
}
# Generate a passphrase
LASTLINE="${LINENO}"
export PASSPHRASE=$(head -c 500 /dev/urandom | tr -dc a-z0-9A-Z | head -c 128; echo)
fail_if_error $?
echo -n "Passphrase expoted as "
printenv PASSPHRASE
# Certificate details; replace items in angle brackets with your own info
subj="
C=IR
ST=Alborz
O=ACME
localityName=Karaj
commonName=*.$PCODE.dev
organizationalUnitName=WebAdmin
emailAddress=noreply#$PCODE.dev
"
LASTLINE="${LINENO}"
# Generate the server private key
openssl genrsa -des3 -out certs/$PCODE.key -passout env:PASSPHRASE 2048
fail_if_error $?
LASTLINE="${LINENO}"
# Generate the CSR
openssl req \
-new \
-batch \
-subj "$(echo -n "$subj" | tr "\n" "/")" \
-key certs/$PCODE.key \
-out certs/$PCODE.csr \
-passin env:PASSPHRASE
fail_if_error $?
LASTLINE="${LINENO}"
cp certs/$PCODE.key certs/$PCODE.key.org
fail_if_error $?
LASTLINE="${LINENO}"
# Strip the password so we don't have to type it every time we restart Apache
openssl rsa -in certs/$PCODE.key.org -out certs/$PCODE.key -passin env:PASSPHRASE
fail_if_error $?
LASTLINE="${LINENO}"
# Generate the cert (good for 10 years)
openssl x509 -req -days 3650 -in certs/$PCODE.csr -signkey certs/$PCODE.key -out certs/$PCODE.crt
fail_if_error $?

The command you want to execute has relative paths, eg: certs/$PCODE.key. When you exec the commands (via the backtick operator in this case), the paths are expanded relative to the PHP process' current working directory. This is rarely, if ever, the same path as your command shell uses.
To debug this, you can extend your actual command with strace, eg: strace openssl .... This will give you considerable diagnostics and, near the end, you'll see something along the lines of EPERM.
To fix this, you can either use chdir in your PHP to set the current working directory, or you can cd in your script, or your script can use absolute paths. I'd prefer the latter.

Related

Replace .env parameter with content of a file

I want to replace a parameter in my .env with content of a file when installing dependencies.
I've this in my docker-compose.yml under the specific php-container:
command:
- /bin/sh
- -c
- |
date +%s | sha256sum | base64 | head -c 32 > passphrase.txt
openssl genrsa -out config/jwt/private.pem -aes256 -passout file:passphrase.txt 4096
openssl rsa -passin file:passphrase.txt -pubout -in config/jwt/private.pem -out config/jwt/public.pem
composer install
php bin/console --no-interaction doctrine:migrations:migrate
Now when composer runs, the parameter of JWT_PASSPHRASE should be replaced with the content of passphrase.txt:
###> lexik/jwt-authentication-bundle ###
# Key paths should be relative to the project directory
JWT_PRIVATE_KEY_PATH=config/jwt/private.pem
JWT_PUBLIC_KEY_PATH=config/jwt/public.pem
JWT_PASSPHRASE=???
JWT_TOKEN_TTL=3600
###< lexik/jwt-authentication-bundle ###
Is that possible?
Best,
Christian
I solved this just by exporting the JWT_PASSPHRASE. I do not need to write the passphrase into a file.
export JWT_PASSPHRASE=$(date +%s | sha256sum | base64 | head -c 32)
openssl genrsa -out config/jwt/private.pem -aes256 -passout pass:${JWT_PASSPHRASE}
openssl rsa -passin pass:${JWT_PASSPHRASE} -pubout -in ./config/jwt/private.pem -out ./config/jwt/public.pem

PHP escapeshellarg messing up my password

$passphrase = "';__!!??()[]";
$passphrase = escapeshellarg($passphrase);
shell_exec("openssl\openssl.exe genrsa -des3 -passout pass:${passphrase} -out test.key 2048");
#Here the password works
echo system("openssl\openssl.exe rsa -in test.key -passin pass:${passphrase} -noout -text");
This code works fine to generate a key with openssl. I can also read the key without any problem. But when I want to read the key from the command line I'm unable to decrypt it. I use exactly the same command as in the code. The only difference is, that I copy the passphrase to the command line as it is written in the code. This always fails with a bad decrypt error.
How can I fix this issue?
Edit: To make this more clear. This does not work when run from terminal:
openssl\openssl.exe rsa -in test.key -passin pass:"';__!!??()[]" -noout -text
Why don't you use PHP's OpenSSL library instead of the shell?
http://php.net/manual/en/ref.openssl.php

PHP shell command not working

I am trying to run the following command from PHP
openssl enc -base64 -in <( openssl dgst -sha256 -binary -hmac echkrHQpoaK8LV4ROplB8vQiY9r1dQf0 <( echo -n "POST application/json a5d3867576a72ad3d981f91032f32aff6b81435ea226807e32f322b20b711a54 2017-02-22T16:55:14Z /user/2"))
But I keep getting the error
sh: -c: line 0: syntax error near unexpected token `('
sh: -c: line 0: `openssl enc -base64 -in <( openssl dgst -sha256 -binary -hmac echkrHQpoaK8LV4ROplB8vQiY9r1dQf0 <( echo -n "POST application/json a5d3867576a72ad3d981f91032f32aff6b81435ea226807e32f322b20b711a54 2017-02-22T16:55:14Z /transaction/v23"))'
I have used shell_exc(), exec(), system() and backticks `` and they all give me the same output.
I am using PHP 7 on an Ubuntu 16.04 server and also in a CentOS 7 server.
The command runs as expected with a valid output when run directly in the shell.
I am at a lost here. Any help will be appreciate it.

Running script through shell_exec not giving the same output is when running on the server

I'm having as issue with running a script using shell_exec in PHP.
When I log onto the server and run the script I get the correct output but when running it through the webpage it doesn't seem to be completing the commands.
The script is to use openssl to create .pem, .pfx and .p12 files from .crt and .key files.
Bash Script is below:
#!/bin/bash
#Script to create all the SSL certs needed from the .key and .crt files
set -o errexit
echo "Starting script....<br><br>"
echo "openssl pkcs12 -export -in $1.crt -inkey $1.key -out $1.p12 -passout pass:$2"
openssl pkcs12 -export -in $1.crt -inkey $1.key -out $1.p12 -passout pass:$2
echo "P12 Complete.<br><br>"
openssl pkcs12 -in $1.p12 -nodes -out $1.pem -passin pass:$2 -passout pass:$2
echo "PEM Complete.<br><br>"
openssl pkcs12 -inkey $1.pem -in $1.crt -export -out $1.pfx -passout pass:$2
echo "PFX complete.<br><br>"
mkdir $1_certs
mv $1.key $1_certs/$1.key
mv $1.crt $1_certs/$1.crt
mv $1.pem $1_certs/$1.pem
mv $1.p12 $1_certs/$1.p12
mv $1.pfx $1_certs/$1.pfx
echo "Password: " $2 >> $1_certs/password.txt
echo "ZIPing files.<br><br>"
zip $1_certs.zip $1_certs
echo "COMPLETE<br><br>"
PHP is below:
<?php
if (isset($_GET['cert_name'])) {
$cert_name = $_GET['cert_name'];
$password = $_GET['password'];
echo "/home/<username>/ssl $cert_name $password <br><br>";
$message=shell_exec("/home/<username>/ssl $cert_name $password");
echo $message;
}
?>
The abundance of echo's in both was to aid in troubleshooting.
The webpage is a basic table with 2 inputs and a submit button.
When I Run this in the webpage it gets to the openssl command to create the .p12 and fails.
If I remove the set -o errexit so that it runs completely through regardless of errors I can see that it doesnt even try to create the directory or move the files, I just see all the echo's. Its as if it just runs the echos and ignores the commands.
I have an echo in before the command to create the p12 file and it shows that it is getting all the correct details.
I'm at a loss of where to go from here. Any help would be appreciated.
From the manual on shell_exec
Return Values
The output from the executed command or NULL if an error occurred or the command produces no output.
Note:
This function can return NULL both when an error occurs or the program produces no output. It is not possible to detect execution failures using this function. exec() should be used when access to the program exit code is required.
In debugging, echo, and print/print_r variations aren't very helpful, because they type cast null to a string. Instead you could use var_dump, which guarantees output for every input even if it is null.
As far as why it's failing there is a way to get back any information from STDERR by redirecting STDERR to STDOUT.
$message = shell_exec("/home/<username>/ssl $cert_name $password 2>&1");
It's also important to note that you should escape any arguments passed to the shell via escapeshellarg
If I had to guess at why it's failing my best guess would be that whatever user PHP is running under (if you're doing this via your web server, for example, and using Apache httpd with mod_php, it probably doesn't have the necessary permissions to execute /home/<username>/ssl. You should be able to determine that for sure by checking permissions on the file and confirming with STDERR information back from the shell.

OpenSSL SHA1 Hash doesn't match

I'm generating a SHA1 hash via OpenSSL via the commandline with the following command:
echo -n "test" | openssl dgst -sha1 -sign private.pem | openssl enc -base64
The output is:
mTuk4MicnS1Xn9BB4wed6pWe62CGDgj6imaOp9f3spiRo/W88WNac7sMkAYl37ruh82mbREbEzsFwCCdhO3MpGh/tyhb+2vx59tta1GTp5Nhb8PlnFL20Zh8QUrv6WrgvsI8z4IPG4KXCJw++7hBQHcnxa8dT5EMn1OW72MumG8=
when I execute the same command via PHP with exec() I get a different output:
YDGDpc0nC1uaFBO28uepQ/8hMhqoUhXIhqb0UTVCHA2oqWI7PeYyHBB1tmvQ8iqo/ZJzvkNxAruy6T67rdpz/4hyKh6hRxGvYNStteqv/Cn04yiSlgidiHnN2x5aoI6GdE/c0haiE/WmJlFTOcQdPztsQWOk2QUzWdwDmO0OjqE=
WHY?
both scripts run via the same user, As the PHP Script is run as "nobody" I have logged in via the shell as nobody and executed it... no dfference
Using the full path fixed the problem!

Categories