Command Line unix stuff over my head: What am I doing wrong?

TL;DR summary: I have instructions for how to retro-certify my older operating system so the expired certificates won’t get in my way any more. I am having problems implementing them and could use help.


I continue to use MacOS 10.11 as my everyday operating system. Not too long ago, a lot of web sites stopped playing nicely with most of my browsers because the security certificates on the computer are considered expired. Firefox bails me out — it just doesn’t care. But I don’t much care for Firefox.

On this web site are explicit instructions for how to retro-certify an older Mac OS so that this problem goes away.

Instrux boil down to four steps:

a) “launch Keychain Access, select “System Roots”, select all the certificates, select File->Export, and export them as rootcerts.pem file. This file will contain all the certificates concatenated.
Copy the rootcerts.pem file to your antique mac”

Did that, no problem.

b) “Make the trustroot shell script below, e.g. by copying it into a file:”

mkdir -p ${DIR}
trap "rm -rf ${DIR}" EXIT
cat "$1" | (cd $DIR && split -p '-----BEGIN CERTIFICATE-----' - cert- )
for c in ${DIR}/cert-* ; do
   security -v add-trusted-cert -d -r trustRoot -k "/Library/Keychains/System.keychain" "$c"
rm -rf ${DIR}

I gathered I was supposed to substitute actual values for the stuff in {curlyquotes}, so:

#!/bin/bash DIR=$/Users/ahunter3/Documents/ElCap/trustroot.$$ mkdir -p $/Users/ahunter3/Documents/ElCap/Here trap "rm -rf $/Users/ahunter3/Documents/ElCap/trustroot.$$" EXIT cat "$1" | (cd $DIR && split -p '-----BEGIN CERTIFICATE-----' - cert- ) for c in $/Users/ahunter3/Documents/ElCap/trustroot.$$/cert-* ; do security -v add-trusted-cert -d -r trustRoot -k "/Library/Keychains/System.keychain" "$c" done rm -rf $/Users/ahunter3/Documents/ElCap/trustroot.$$

I found it bewildering that the instrux didn’t say where to put the damn file or what to name it. But umm okay… I created a folder within ~/Documents and called it “ElCap” (because MacOS 10.11.x is nicknamed “El Capitan”) and within that saved the above incoherent-to-me batch file command stuff as a file named “hi i am a file”.

c) “then using chmod 755 trustroot…”

They sure could’ve been a lot more explicit about that, but I know the chmod command. So I did that:

d) “…Run sudo ./trustroot rootcerts.pem”

They could’ve been a lot more explicit about that too but I had a terminal window open already so what the hell…

As you can see from the Terminal output, there are problems. I’ve done something wrong somewhere.

One clue is that a set of folders in a folder hierarchy were generated, and from their names I think something did not get placed or executed at the correct point in the hierarchy?:

Note that all of these folders are inside the folder I created called “ElCap”. So inside of ElCap is a folder named “Users”, within that “ahunter3”, within that “Documents”, within that “ElCap”, and then a folder named “Here”. I’m pretty sure “Here” was supposed to have been created within the parent “ElCap” folder. But I don’t think simply moving it will fix anything in and of itself.

Anyone in a position to sort me out on this?

The shell script is complete as it is. The things in the brackets are variables that will create a temporary directory, do some stuff in it, then delete the temp directory. Just copy the full text into a file named trustroot without replacing things in the {} brackets.

Then do chmod 755 trustroot on the file you created, and then run the sudo trustroot rootcerts.pem command.

Note: I don’t vouch for the process since I don’t understand it. Run sudo script_i_downloaded_from_the_internet at your own risk. Nothing obviously jumps out as nefarious to me.

Modified the file to contain the shell script as posted instead of with stuff in curly brackets replaced. re- chmodded it.

Running the sudo command from within the folder containing the newly modified and newly chmodded textfile makes Terminal issue these reactions:

Reaver:ElCap ahunter3$ sudo ./trustroot rootcerts.pem
./trustroot: line 5: cd: $/Users/ahunter3/Documents/ElCap/trustroot.21375: No such file or directory
add-trusted-cert "-d" "-r" "trustRoot" "-k" "/Library/Keychains/System.keychain" "$/Users/ahunter3/Documents/ElCap/trustroot.21375/cert-*"
***Error reading file $/Users/ahunter3/Documents/ElCap/trustroot.21375/cert-*
Error reading file $/Users/ahunter3/Documents/ElCap/trustroot.21375/cert-*

changing directories back to root and trying again (since as I said, the folders created seem to indicate that something happened in the wrong place within my directory hierarchy) just gives me this:

Reaver:ElCap ahunter3$ cd /
Reaver:/ ahunter3$ sudo ./trustroot rootcerts.pem
sudo: ./trustroot: command not found

Don’t create directories with $ in the name (unless you want to :slight_smile: :slight_smile: ) NB you do not need to manually create any special directories for the script you posted to run.

And, yes, use the original unmodified shell script; don’t start hacking it just yet. Maybe also close that terminal and re-open a new one and start from the beginning.

I assume you did not save the script in the root directory, nor would you want to; cd to Documents/ElCap or wherever you put all that stuff.

It might be worthwhile explaining the script. Then its use might be more clear.

Anything that starts with a $ is a variable, and its value is substituted for the string. Sometimes the shell can’t easily work out where the variable name stops and the next bit of text starts, so another way of referencing a variable is to add braces ({}) around the name. So $DIR and ${DIR} are the same thing. The script is a little lazy is having both uses. It is common to always use braces just in case a problem arises in the future.

The shell provides a few useful predefines variables.
TMPDIR is a path that will reference a valid place to put temporary things. You don’t need to define it yourself. The shell sorts it out for you. If you want to see where it is use:
$ echo $TMPDIR

$$ is another special variable and is the process id of the running shell. So you get a sort of unique ID that can be used to avoid conflicts amongst other things.

#   #! is a magic number, it occupies the first two bytes of the file, and tells the OS that this file 
#   should be executed by the program whose path follows. In this case the bash shell

# create the variable called DIR using the values of TMPDIR path and $$

# Create the directory whose path is the contents of the string DIR, making any needed intermediate directories
mkdir -p ${DIR}   

trap "rm -rf ${DIR}" EXIT  # If anything goes wrong from here on in, just remove the temp directory and exit

# Using the 1st parameter on the command line that invoked this script - the variable $1 - send the value of that 
# parameter into a compound command. That command changes directory to the newly created temp dir - DIR - 
# and uses the split command to break the file previously created full of certificates into individual files, one per 
# certificate. These are all put in the temp directory, since that is where the cd put the running of split.
cat "$1" | (cd $DIR && split -p '-----BEGIN CERTIFICATE-----' - cert- )  

# Now, for each file in the temp directory whose name starts with cert- add those certificates to your MacOS 
# keychain. Note how the variable c is created by the for loop and accessed via $c, thus giving each file created above to the security program in sequence
for c in ${DIR}/cert-* ; do
   security -v add-trusted-cert -d -r trustRoot -k "/Library/Keychains/System.keychain" "$c"

rm -rf ${DIR}  # Remove the temp dir and its contents

You don’t need to, and should not, modify the script. All you have to do is run it and provide the name of the file you created with all the certificates in it.

Note, On any Unix system, and that includes MacOS, don’t put spaces in file names. It is a just plain bad idea and can cause all manner of nasty things to break. You can never guarantee that a script won’t interpret a file name as a series of separate strings, which can lead to arbitrary grief.

So, this part of the script

mkdir -p ${DIR}

Should make a variable called DIR with a path to a temporary directory with the PID (a number that designates the script process that’s running), then make a directory named that. Then the rest of the script uses that directory.

When you ran it that last time, the PID Must have been 21375, because it tried to use trustroot.21375 as the tempdir. But then it couldn’t find that dir, so something went wrong.

As a sanity check:
What’s the output of
echo $TMPDIR

If you make a script that just includes

echo ${DIR}
mkdir -p ${DIR}

name it make_tmp_dir or something, chmod it, then run it

What does it output, and does it make a directory that matches the line it outputs?

You need to be very, very careful with stuff like this if you don’t know what you’re doing. Hacking up a script that includes “rm -rf” commands and then running it as root could have easily wiped your entire hard drive (including any external drives connected at the time, and connected network servers) due to a typo as small as one space character in the exact wrong spot. Do you have offline backups?

Yes, bootable ones at that, but thanks for asking and for pointing that out.

This is a good reason why you don’t want spaces in file names.

Unless you carefully escape the spaces (backslash in front of every space) the shell and programs will consider each word of the file name as a separate parameter.

A command like rm will cheerfully go looking for a series of files with those names. It doesn’t take much to end in tears. Especially using rm -rf as the superuser. No mercy is shown.

The classic boo-boo was typing something like rm -rf foo * when of course one meant rm -rf foo.* (Yes, users did this, and the sysadmin, who was me, had to get the backup tapes to fix them.)

Bonus points doing this as superuser in the root directory.

And this is why rm and other such commands will prompt you if you want to actually run them when you do something this likely to be destructive.

Except it won’t if you pass -f to it.

But you didnt run that file… The error quoted shows the same problem introduced by your removal of the curly brackets … You didnt remove the dollar sign when trying to convert the script variables into full values, and your second attempt shows the same error, leading me to think what you ran was no different. Why ? At one point, you created the file “this is a file” when you were instructed to call it “trustroot”, and you never clarified how you corrected for that. Seems you ensured “this is a file” was what was given to and then ran “trustroot” ? but trustroot was still your broken edited version.

DIR= sets the variable, and then dollar curly brackets DIR uses the variable. So when you “removed curly brackets” you needed to “remove dollar curly brackets”, but for the 2nd attempt, you needed to ensure the file installroot is letter perfect.

Thank you for stepping in. In order for me to make any use of what you just said, you’re going to have to back away from the notion that I understand diddly squat about the language bash. I don’t. What would be most useful would be extremely specific Step One, Step Two instructions, of the sort that you’d give to someone who knows how to use a keyboard and a mouse. If you don’t feel like doing that degree of handholding I completely understand.

You say I was instructed to create a file and call it “trustroot” ??? I was??? (not in any language that I understand! that was one of my concerns, that I was being told to create a file but not told what to name it or where to put it!)

Forget about bash for the moment.

Are you comfortable with the concept of creating and editing ASCII text files in general? However you prefer to do it, doesn’t really matter for our immediate purposes. When you save it you have to give it a name: could be trustroot, My_novel, whatever.

ETA if you need a free editor there are Atom, MacVim, Textmate, …

I have BBEdit, which is what I used for this. Plain text editor. In the absence of any clear (to me) instructions about where to save it or what to call it, I gave it the silly name of “hi i am a file”

You can see a screenshot of the file’s name in the original post. I saved it to a newly created “ElCap” folder within my own Documents folder.

Yeah, the instructions just assume you know to save it as a file named trustroot. That is sloppy instruction writing.

Go back to the original stackexchange post, copy all of the text in the code box, and paste it into BBEdit. When you are done the BBEdit window should contain exactly what is in that code box. Every - * " $ and { are important, so don’t change any of them. Save the file you’ve created in BBEdit with the name trustroot

Once that is done, you should have a directory containing the trustroot file you just created, and the rootcerts.pem file that you extracted from a donor Mac.

Then open a terminal and use cd to go to the directory where you saved your two files. Then run the command

$ sudo ./trustroot rootcerts.pem

(The $ in the above command represents the prompt at the command line, don’t type that part.)

If you’re not sure how to do something, please ask. If I’d been writing out those instructions for my users, they would have been about 3 pages long, just to cover all the little steps that get glossed over, but are horribly confusing to people who don’t “just know” what to do.

no no no. Don’t gather anything if you don’t understand what you’re doing. These are variables and the script will de-reference them as needed. Your system already knows what ${TMPDIR} is. That’s where you went off the rails initially.

Now, looking at the script, I think I see a logical error. The script expects to find your rootcert.pem file under a certain path name (where you put the file before you ran it). But on line 2 of the script, it composes a path name using the process ID of the script. That’s the $$ variable - it’s an ephemeral value that is only assigned while the script is running.

If the directory name wasn’t known until the script was run, how would you know where to put the file?
You couldn’t. Ergo, the script is flawed.

I think this will go better if you modify line 2 of the script to read:

Then before you run the script, you will put your downloaded rootcert.pem in /tmp/trustroot exactly as you specified on line 2 of your script.

I suspect that this script is a fragment that was pulled out of a larger original script that actually performed the downloading of rootcert.pem for you. Then it would have all the information it needed. But in its current form, it doesn’t do that for you, and it assumes you know information that you can’t know. So it’s wrong. But easy to fix as I showed above.

I followed echoreply’s instructions and judging from the Terminal output it did exactly what it’s supposed to do :slight_smile:

All I needed was the explanations that

a) the contents of the file are to be exactly what is shown in the screen*; and
b) the damn file is to be named “trustroot”

I suppose the first of those would have been more self-explanatory to anyone who has not worked in an environment where things you’re supposed to replace with the real values are enclosed in curly braces.

And it works (I’m no longer being blocked from sites that don’t consider me eligible to make an https connection due to expired certificates) :slight_smile: