4

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/.../[email protected]  
 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 $?
8
  • 1
    I bet PHP is running from a privileged current working directory, as the failing command contains a relative path: certs/$PCODE.key. What happens if you make that an absolute path? Or, alternatively, do a chdir('/tmp/') in PHP? Commented Oct 13, 2017 at 14:16
  • Your "From PHP" code contains ticks ` which should be quotes. That alone should be throwing you an error php.net/manual/en/function.error-reporting.php << "How can I debug it?". Commented Oct 13, 2017 at 14:18
  • 1
    @Fred-ii- Please see, php.net/manual/en/language.operators.execution.php backtick is used to execute shell commands which I believe OP is trying to do. Commented Oct 13, 2017 at 14:20
  • @mega6382 I stand corrected. Commented Oct 13, 2017 at 14:20
  • 1
    @bishop sharp point! I just converted all paths to absolutes and am getting one command further (Command after line 49 executed oK Error. Return value = 1 after line 54). Will shortly update my question. Commented Oct 13, 2017 at 14:24

1 Answer 1

3

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.

Sign up to request clarification or add additional context in comments.

1 Comment

I wish I could upvote this more than once. Accept will follow :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.