Wednesday, December 29, 2010

gpg-agent on OS X

I don't like to have username and passwords lying around unencrypted on my hard drive. But I also don't like to have to keep entering in the same information over and over.

A great example of this problem can be found in the Emacs Gnus Mail Reader. Gnus lets you store your username and password information in a file called .authinfo. This way you don't have to enter a password every time you connect to your news or email servers. Of course the file isn't encrypted. EasyPG to the rescue. EasyPG allows you to have any file named *.gpg and it will then use gpg to decrypt the file. One little problem, every time Gnus needs to read info from the .authinfo file it prompts for the gpg password. So although you don't have to remember the passwords for the accounts you have to keep entering your gpg password. Ugghhh.

One way to fix this is to use gpg-agent. gpg-agent is a daemon to manage gpg private keys independently of any protocol. So with this you can configure how long to cache an entry (like a passphrase) so you don't have to enter it again. By default gpg-agent has a cache-ttl of 600 seconds. With this working Gnus can read the encrypted .authinfo file for 5 minutes and only prompts you for a password on the first read. Pretty sweet.

Of course setting up gpg-agent on OS X so that it interacts nicely with GUI apps like Emacs.app as well as command line utilities isn't perfectly straightforward but hopefully this entry will help.

Getting the software

I'm a big fan of MacPort so I'll use that in the examples. But you could download and compile your self.

sudo port install pinentry +gtk2 gpg-agent gnupg2

You need to add one of the variants to pinentry to get a GUI client that works on OS X. Your choices are: gtk1, gtk2, qt3 or qt4. I used gtk2 because I have other ports that depend on it but you can pick your favorite GUI toolkit. If you don't select a variant you will get a pinentry application that can only be run from a terminal.

Setup Emacs to use EasyPG and gpg-agents

The first step is to get Emacs to use EasyPG on files that end in .gpg. More info can be found on the Emacs Wiki.

Add the following to your Emacs.app start-up file

(require 'epa-file)
(epa-file-enable)

Next you need to teach EasyPG to use gpg2 instead of gpg as the name of the gpg executable. This is because MacPorts installs the gpg executable as gpg2 for the gnupg2 port. There is a gnupg port that installs the gpg executable but that is version 1.4 and doesn't work by default with gpg-agent.

Add the following to your Emacs.app start-up file

(setq epg-gpg-program "gpg2")

Lastly you need to teach Emacs how to talk to gpg-agent.

Add the following to your Emacs.app start-up file

(setenv "GPG_AGENT_INFO" "~/.gnupg/S.gpg-agent")

Getting gpg-agent to start at Login

Now that Emacs.app is configured to use gpg and gpg-agent you have to configure it so gpg-agent is started when you login. There are several ways to do this. I think the easiest and most "Macish" is to add a new Login Item in the Users Preference Pane. But to do that it has to be an Application. Luckily, AppleScript makes it easy to make Applications out of simple scripts.

A script to start gpg-agent

First lets write a simple Bash script to start gpg-agent

#!/bin/bash

GPG_AGENT=/opt/local/bin/gpg-agent
GPG_ARGS="--daemon --use-standard-socket"
GPG_SOCKET="~/.gnupg/S.gpg-agent"

if [[ -e ${GPG_SOCKET} ]]
then
    rm ${GPG_SOCKET}
fi

if [[ -x ${GPG_AGENT} ]]
then
    ${GPG_AGENT} ${GPG_ARGS}
fi

Save this to a file named gpg_agent_start.sh in your bin directory and make it executable.

chmod 755 ~/bin/gpg_agent_start.sh

Applescript to make an Application

Next start "AppleScriptEditor" it can be found in your /Applications/Utilities folder. Make a new script with the following:

do shell script "~/bin/gpg_agent_start.sh"

And then do a "Save As …", be sure to select "Application" as the File Format and name it "gpg_agent_Application".

Set as Login Item

  1. Launch System Preferences
  2. Select Accounts
  3. Select Login Items
  4. Use the "+" to add gpg_agent_Application
  5. Close System Preferences
  6. Logout and back in

You should see gpg-agent as a running process.

At this point you should be able to open any encrypted file named *.gpg and get prompted for the password with the pinentry application. Then any access to that file in the next 5 minutes won't require entering a password.

Monday, November 8, 2010

Setting up Emacs.app

I use OS X at both work and home, and I'm a dedicated GNU Emacs user. There are several ways to run GNU Emacs under OS X, but I like Emacs.app (or GNU Emacs/Cocoa or GNU Emacs/nextstep if you prefer). This means Emacs runs just like a normal OS X application and is fully integrated into the Finder. This has one little annoyance though. If you have installed other command line utilities in places other than /usr/bin, /bin, /usr/sbin or /sbin Emacs.app can't find them. Why you ask? Because by default that is what the Finder sets your PATH environment variable to be. So there are two ways to fix this.

Add new paths to the Finder

This is pretty easy. Just follow the directions here: http://developer.apple.com/library/mac/#qa/qa2001/qa1067.html

You will probably want to add paths for: PATH, MANPATH and INFOPATH

The downside of this approach is now you have set the environment variable for every application that is run from the Finder. That may or may not be what you want. Also you have to force the Finder to re-read the plist file every time you want to make a change. That usually means logging out and back in.

Add paths to Emacs.app

So Emacs has some internal variables that it uses to track these paths. We can just modify them in Emacs itself, and tweak them whenever we want to.

Finding new executables

Emacs uses the exec-path variable to find any additional utilities it needs. Here is some example code for how to add paths to the exec-path variable.

;;; Add directories to exec-path
(mapcar '(lambda (x)
           (add-to-list 'exec-path x))
        '("/usr/local/bin"
          "/opt/local/sbin"
          "/opt/local/bin"
          "~/bin"))

If you run shells inside of Emacs, you may want to modify the PATH variable as well. Why? Well if you run a shell inside Emacs it will use the PATH variable to find things not the exec-path variable. Here is some example code for that:

;;; Setup PATH variable
(mapcar (lambda (x)
          (setenv "PATH"
                  (concat x ":" (getenv "PATH"))))
        '("/usr/local/bin"
          "/opt/local/sbin"
          "/opt/local/bin"
          "~/bin"))

Finding new man pages

Emacs.app uses the MANPATH variable to find manpages, here is some example code on how to modify it:

;;; Setup MANPATH variable
(mapcar (lambda (x)
          (setenv "MANPATH"
                  (concat x ":" (getenv "MANPATH"))))
        '("/opt/local/man"
          "~/man"))

Finding new info pages

Emacs.app uses the Info-directory-list variable to find info pages. Here is some example code:

;;; Setup info path
(require 'info)
(mapcar '(lambda (x)
           (add-to-list 'Info-directory-list x))
        '("/usr/share/info"
          "/opt/local/share/info"
          "/Applications/Emacs.app/Contents/Resources/info"
          "~/info"))

This one is a little trickier than the others. First you have to load the info package so that the Info-directory-list is defined. Then you should also add the info directory from the Emacs.app itself.