-monkeysphere (0.2-1) UNRELEASED; urgency=low
+monkeysphere (0.4-1) UNRELEASED; urgency=low
+
+ [Daniel Kahn Gillmor]
+ * New version (switch UNRELEASED to experimental when ready)
+
+ -- Daniel Kahn Gillmor <dkg-debian.org@fifthhorseman.net> Tue, 24 Jun 2008 01:25:45 -0400
+
+monkeysphere (0.3-1) experimental; urgency=low
+
+ [ Daniel Kahn Gillmor ]
+ * new version.
+
+ [ Jameson Graef Rollins ]
+ * Move files in /var/cache/monkeysphere and GNUPGHOME for server to
+ the more appropriate /var/lib/monkeysphere.
+
+ -- Daniel Kahn Gillmor <dkg-debian.org@fifthhorseman.net> Tue, 24 Jun 2008 00:55:29 -0400
+
+monkeysphere (0.2-2) experimental; urgency=low
+
+ * added lockfile-progs dependency
+
+ -- Daniel Kahn Gillmor <dkg-debian.org@fifthhorseman.net> Mon, 23 Jun 2008 19:34:05 -0400
+
+monkeysphere (0.2-1) experimental; urgency=low
[ Daniel Kahn Gillmor ]
- * NOT YET RELEASED (switch to "experimental" when ready to release)
+ * openpgp2ssh now supports specifying keys by full fingerprint.
[ Jameson Graef Rollins ]
* Add AUTHORIZED_USER_IDS config variable for server, which defaults to
* Better failure/prompting for gen-subkey
* Add ability to set any owner trust level for keys in server keychain.
- -- Jameson Graef Rollins <jrollins@phys.columbia.edu> Sun, 22 Jun 2008 11:42:42 -0400
+ -- Daniel Kahn Gillmor <dkg-debian.org@fifthhorseman.net> Mon, 23 Jun 2008 17:03:19 -0400
monkeysphere (0.1-1) experimental; urgency=low
Priority: extra
Maintainer: Daniel Kahn Gillmor <dkg-debian.org@fifthhorseman.net>
Uploaders: Jameson Rollins <jrollins@fifthhorseman.net>
-Build-Depends: debhelper (>= 7.0), libgnutls-dev (>= 2.3.14)
+Build-Depends: debhelper (>= 7.0), libgnutls-dev (>= 2.4.0)
Standards-Version: 3.8.0.1
Homepage: http://cmrg.fifthhorseman.net/wiki/OpenPGPandSSH
Dm-Upload-Allowed: yes
Package: monkeysphere
Architecture: any
-Depends: openssh-client, gnupg | gnupg2, coreutils (>= 6), moreutils, ${shlibs:Depends}
+Depends: openssh-client, gnupg | gnupg2, coreutils (>= 6), moreutils, lockfile-progs, ${shlibs:Depends}
Recommends: netcat
Enhances: openssh-client, openssh-server
Description: use the OpenPGP web of trust to verify ssh connections
-var/cache/monkeysphere
-var/cache/monkeysphere/authorized_keys
+var/lib/monkeysphere
+var/lib/monkeysphere/authorized_keys
usr/bin
usr/sbin
usr/share
usr/share/monkeysphere
-var/cache/monkeysphere
-var/cache/monkeysphere/authorized_keys
+var/lib/monkeysphere
+var/lib/monkeysphere/authorized_keys
etc/monkeysphere
and how the monkeysphere can help you reduce these threat vectors:
threat model reduction diagrams.
-Determine how openssh handles multiple processes writing to
- known_hosts/authorized_keys files (lockfile, atomic appends?)
-
Handle unverified monkeysphere hosts in such a way that they're not
always removed from known_hosts file. Ask user to lsign the host
key?
expects from authorized_keys: we don't want monkeysphere to be a
weak link in the filesystem.
-What happens when a user account has no corresponding
- /etc/monkeysphere/authorized_user_ids/$USER file? What gets placed
- in /var/cache/monkeysphere/authorized_keys/$USER? It looks
- currently untouched, which could mean bad things for such a user.
- - if authorized_user_ids is empty, then the user's authorized_keys
- file will be also, unless the user-controlled authorized_keys file
- is added. I believe this is expected, correct behavior.
-
Consider the default permissions for
- /var/cache/monkeysphere/authorized_keys/* (and indeed the whole
+ /var/lib/monkeysphere/authorized_keys/* (and indeed the whole
directory path leading up to that)
-As an administrator, how do i reverse the effect of a
- "monkeysphere-server trust-keys" that i later decide i should not
- have run?
-
Make sure alternate ports are handled for known_hosts.
Script to import private key into ssh agent.
Update monkeysphere-ssh-proxycommand man page with new keyserver
checking policy info.
-Update monkeysphere-ssh-proxycommand man page with info about
- no-connect option.
-
File bug against seahorse about how, when creating new primary keys,
it presents option for "RSA (sign only)" but then creates an "esca"
key.
File bug against enigmail about lack of ability to create subkeys.
-Priviledge separation: monkeysphere user to handle authn keyring and
+Privilege separation: monkeysphere user to handle authn keyring and
generate authorized_keys file (which would be moved into place by
root). Host keyring would be owned by root.
-Check permissions of authorized_user_ids file to be writable only by
- user and root (same as authorized_keys)
+Test and document what happens when any filesystem that the
+ monkeysphere-server relies on and modifies (/tmp, /etc, and /var?)
+ fills up.
-Improve function that sets owner trust for keys in server keychain.
+Optimize keyserver access, particularly on monkeysphere-server
+ update-users -- is there a way to query the keyserver all in a
+ chunk?
* Please add new entries in reverse chronological order whenever you make *
* changes to this system *
******************************************************************************
-
+2008-06-23 - dkg
+ * added monkeysphere apt repository to /etc/apt/sources.list
+ * added dkg's key to apt's list of trusted keys.
+ * ran aptitude dist-upgrade
+ * upgraded to monkeysphere 0.2-1
+ * moved authorized_user_ids files into users' home directories.
+ * installed lockfile-progs
+
2008-06-22 - dkg
* installed screen (mjgoins and i were collaborating)
# This is an sh-style shell configuration file. Variable names should
# be separated from their assignements by a single '=' and no spaces.
-# GPG home directory for server
-#GNUPGHOME=/etc/monkeysphere/gnupg
-
# GPG keyserver to search for keys
#KEYSERVER=subkeys.pgp.net
-# Required user key capabilities
-# Must be quoted, lowercase, space-seperated list of the following:
-# e = encrypt
-# s = sign
-# c = certify
-# a = authentication
-#REQUIRED_USER_KEY_CAPABILITY="a"
-
# Path to authorized_user_ids file to process to create
# authorized_keys file. '%h' will be replaced by the home directory
# of the user, and %u will be replaced by the username of the user.
# Whether to add user controlled authorized_keys file to
# monkeysphere-generated authorized_keys file. Should be path to file
-# where '%h' will be replaced by the home directory of the user.
-# To not add any user-controlled file, put "-"
-#USER_CONTROLLED_AUTHORIZED_KEYS="%h/.ssh/authorized_keys"
+# where '%h' will be replaced by the home directory of the user or
+# '%u' by the username. To not add any user-controlled file, put "-"
+# FIXME: this usage of "-" contravenes the normal convention where "-"
+# means standard in/out. Why not use "none" or "" instead?
+#RAW_AUTHORIZED_KEYS="%h/.ssh/authorized_keys"
# GPG keyserver to search for keys
#KEYSERVER=subkeys.pgp.net
-# Required key capabilities
-# Must be quoted, lowercase, space-seperated list of the following:
-# e = encrypt
-# s = sign
-# c = certify
-# a = authentication
-#REQUIRED_HOST_KEY_CAPABILITY="a"
-#REQUIRED_USER_KEY_CAPABILITY="a"
+# Set whether or not to check keyservers at every monkeysphere
+# interaction, including all ssh connections if you use the
+# monkeysphere-ssh-proxycommand.
+# NOTE: setting CHECK_KEYSERVER to true will leak information about
+# the timing and frequency of your ssh connections to the maintainer
+# of the keyserver.
+#CHECK_KEYSERVER=true
# ssh known_hosts file
#KNOWN_HOSTS=~/.ssh/known_hosts
# Should be "true" or "false"
#HASH_KNOWN_HOSTS=true
-# ssh authorized_keys file
-#AUTHORIZED_KEYS=~/.ssh/known_hosts
-
-# This overrides other environment variables
-# NOTE: there is leakage
-#CHECK_KEYRING=true
+# ssh authorized_keys file (FIXME: why is this relevant in this file?)
+#AUTHORIZED_KEYS=~/.ssh/authorized_keys
.Pp
If the data on standard input contains no subkeys, you can invoke
.Nm
-without arguments. If the data on standard input contains
-multiple keys (e.g. a primary key and associated subkeys), you must
-specify a specific OpenPGP keyid (e.g. CCD2ED94D21739E9) or
-fingerprint as the first argument to indicate which key to export.
-The keyid must be exactly 16 hex characters.
+without arguments. If the data on standard input contains multiple
+keys (e.g. a primary key and associated subkeys), you must specify a
+specific OpenPGP key identifier as the first argument to indicate
+which key to export. The key ID is normally the 40 hex digit OpenPGP
+fingerprint of the key or subkey desired, but
+.Nm
+will accept as few as the last 8 digits of the fingerprint as a key
+ID.
.Pp
If the input contains an OpenPGP RSA or DSA public key, it will be
converted to the OpenSSH-style single-line keystring, prefixed with
.Nm
currently cannot handle passphrase-protected secret keys on input.
.Pp
-It would be nice to be able to use keyids shorter or longer than 16
-hex characters.
+Key identifiers consisting of an odd number of hex digits are not
+accepted. Users who use a key ID with a standard length of 8, 16, or
+40 hex digits should not be affected by this.
.Pp
.Nm
only acts on keys associated with the first primary key
/var/cache/monkeysphere/authorized_keys/USER. See `man monkeysphere'
for more info. If the USER_CONTROLLED_AUTHORIZED_KEYS variable is
set, then a user-controlled authorized_keys file (usually
-~USER/.ssh/authorized_keys) is added to the authorized_keys file. `k'
-may be used in place of `update-known_hosts'.
+~USER/.ssh/authorized_keys) is added to the authorized_keys file. `u'
+may be used in place of `update-users.
.TP
.B gen-key
Generate a gpg key for the host. `g' may be used in place of
subcommand. Finally, you need to modify the sshd_config to tell sshd
where the new server host key:
-HostKey /etc/monkeysphere/ssh_host_rsa_key
+HostKey /var/lib/monkeysphere/ssh_host_rsa_key
If the server will also handle user authentication through
monkeysphere-generated authorized_keys files, set the following:
-AuthorizedKeysFile /var/cache/monkeysphere/authorized_keys/%u
+AuthorizedKeysFile /var/lib/monkeysphere/authorized_keys/%u
Once those changes are made, restart the ssh server.
/etc/monkeysphere/monkeysphere.conf
System-wide monkeysphere config file.
.TP
-/etc/monkeysphere/gnupg
-Monkeysphere GNUPG home directory.
+/var/lib/monkeysphere/authorized_keys/USER
+Monkeysphere-generated user authorized_keys files.
.TP
-/etc/monkeysphere/ssh_host_rsa_key
-Copy of the host's private key in ssh format, suitable for use by sshd.
+/var/lib/monkeysphere/ssh_host_rsa_key
+Copy of the host's private key in ssh format, suitable for use by
+sshd.
.TP
-/etc/monkeysphere/authorized_user_ids/USER
-Server maintained authorized_user_ids files for users.
+/var/lib/monkeysphere/gnupg-host
+Monkeysphere host GNUPG home directory.
.TP
-/var/cache/monkeysphere/authorized_keys/USER
-User authorized_keys file.
+/var/lib/monkeysphere/gnupg-authentication
+Monkeysphere authentication GNUPG home directory.
.SH AUTHOR
# managed directories
ETC="/etc/monkeysphere"
export ETC
-CACHE="/var/cache/monkeysphere"
-export CACHE
-ERR=0
-export ERR
########################################################################
### UTILITY FUNCTIONS
echo "$path"
}
-### CONVERTION UTILITIES
+### CONVERSION UTILITIES
# output the ssh key for a given key ID
gpg2ssh() {
local keyID
- #keyID="$1" #TMP
- # only use last 16 characters until openpgp2ssh can take all 40 #TMP
- keyID=$(echo "$1" | cut -c 25-) #TMP
+ keyID="$1"
gpg --export "$keyID" | openpgp2ssh "$keyID" 2> /dev/null
}
fi
requiredPubCapability=$(echo "$requiredCapability" | tr "[:lower:]" "[:upper:]")
- # if CHECK_KEYSERVER variable set, check the keyserver
+ # if CHECK_KEYSERVER variable set to true, check the keyserver
# for the user ID
if [ "$CHECK_KEYSERVER" = "true" ] ; then
gpg_fetch_userid "$userID"
# 0 = ok, 1 = bad
if [ "$keyOK" -a "$uidOK" -a "$lastKeyOK" ] ; then
log " * acceptable key found."
- echo 0 "$fingerprint"
+ echo "0:${fingerprint}"
else
- echo 1 "$fingerprint"
+ echo "1:${fingerprint}"
fi
;;
'sub') # sub keys
# 0 = ok, 1 = bad
if [ "$keyOK" -a "$uidOK" -a "$lastKeyOK" ] ; then
log " * acceptable key found."
- echo 0 "$fingerprint"
+ echo "0:${fingerprint}"
else
- echo 1 "$fingerprint"
+ echo "1:${fingerprint}"
fi
;;
esac
done
}
-# process hosts in the known_host file
-process_hosts_known_hosts() {
+# process a single host in the known_host file
+process_host_known_hosts() {
local host
local userID
local ok
local keyid
local tmpfile
+ local returnCode
+
+ # default return code is 1, which assumes no key was found
+ returnCode=1
+
+ host="$1"
+
+ log "processing host: $host"
+
+ userID="ssh://${host}"
+
+ for line in $(process_user_id "ssh://${host}") ; do
+ ok=$(echo "$line" | cut -d: -f1)
+ keyid=$(echo "$line" | cut -d: -f2)
+
+ sshKey=$(gpg2ssh "$keyid")
+ # remove the old host key line
+ remove_line "$KNOWN_HOSTS" "$sshKey"
+ # if key OK, add new host line
+ if [ "$ok" -eq '0' ] ; then
+ # hash if specified
+ if [ "$HASH_KNOWN_HOSTS" = 'true' ] ; then
+ # FIXME: this is really hackish cause ssh-keygen won't
+ # hash from stdin to stdout
+ tmpfile=$(mktemp)
+ ssh2known_hosts "$host" "$sshKey" > "$tmpfile"
+ ssh-keygen -H -f "$tmpfile" 2> /dev/null
+ cat "$tmpfile" >> "$KNOWN_HOSTS"
+ rm -f "$tmpfile" "${tmpfile}.old"
+ else
+ ssh2known_hosts "$host" "$sshKey" >> "$KNOWN_HOSTS"
+ fi
+ # set return code to be 0, since a key was found
+ returnCode=0
+ fi
+ return "$returnCode"
+ done
+
+ return "$returnCode"
+}
+
+# update the known_hosts file for a set of hosts listed on command
+# line
+update_known_hosts() {
+ local host
+ local returnCode
+
+ # default return code is 0, which assumes a key was found for
+ # every host. code will be set to 1 if a key is not found for at
+ # least one host
+ returnCode=0
+
+ # set the trap to remove any lockfiles on exit
+ trap "lockfile-remove $KNOWN_HOSTS" EXIT
# create a lockfile on known_hosts
lockfile-create "$KNOWN_HOSTS"
for host ; do
- log "processing host: $host"
-
- userID="ssh://${host}"
-
- process_user_id "ssh://${host}" | \
- while read -r ok keyid ; do
- sshKey=$(gpg2ssh "$keyid")
- # remove the old host key line
- remove_line "$KNOWN_HOSTS" "$sshKey"
- # if key OK, add new host line
- if [ "$ok" -eq '0' ] ; then
- # hash if specified
- if [ "$HASH_KNOWN_HOSTS" = 'true' ] ; then
- # FIXME: this is really hackish cause ssh-keygen won't
- # hash from stdin to stdout
- tmpfile=$(mktemp)
- ssh2known_hosts "$host" "$sshKey" > "$tmpfile"
- ssh-keygen -H -f "$tmpfile" 2> /dev/null
- cat "$tmpfile" >> "$KNOWN_HOSTS"
- rm -f "$tmpfile" "${tmpfile}.old"
- else
- ssh2known_hosts "$host" "$sshKey" >> "$KNOWN_HOSTS"
- fi
- fi
- done
+ # process the host, change return code if host key not found
+ process_host_known_hosts "$host" || returnCode=1
+
# touch the lockfile, for good measure.
lockfile-touch --oneshot "$KNOWN_HOSTS"
done
# remove the lockfile
lockfile-remove "$KNOWN_HOSTS"
+
+ return "$returnCode"
+}
+
+# process known_hosts file, going through line-by-line, extract each
+# host, and process with the host processing function
+process_known_hosts() {
+ local returnCode
+
+ # default return code is 0, which assumes a key was found for
+ # every host. code will be set to 1 if a key is not found for at
+ # least one host
+ returnCode=0
+
+ # take all the hosts from the known_hosts file (first field), grep
+ # out all the hashed hosts (lines starting with '|')...
+ for line in $(cat "$KNOWN_HOSTS" | meat | cut -d ' ' -f 1 | grep -v '^|.*$') ; do
+ # break up hosts into separate words
+ update_known_hosts $(echo "$line" | tr , ' ') || returnCode=1
+ done
+
+ return "$returnCode"
}
# process uids for the authorized_keys file
-process_uids_authorized_keys() {
+process_uid_authorized_keys() {
local userID
local ok
local keyid
+ local returnCode
+
+ # default return code is 1, which assumes no key was found
+ returnCode=1
+
+ userID="$1"
+
+ log "processing user ID: $userID"
+
+ for line in $(process_user_id "$userID") ; do
+ ok=$(echo "$line" | cut -d: -f1)
+ keyid=$(echo "$line" | cut -d: -f2)
+
+ sshKey=$(gpg2ssh "$keyid")
+ # remove the old host key line
+ remove_line "$AUTHORIZED_KEYS" "$sshKey"
+ # if key OK, add new host line
+ if [ "$ok" -eq '0' ] ; then
+ ssh2authorized_keys "$userID" "$sshKey" >> "$AUTHORIZED_KEYS"
+
+ # set return code to be 0, since a key was found
+ returnCode=0
+ fi
+ done
+
+ return "$returnCode"
+}
+
+# update the authorized_keys files from a list of user IDs on command
+# line
+update_authorized_keys() {
+ local userID
+ local returnCode
+
+ # default return code is 0, which assumes a key was found for
+ # every user ID. code will be set to 1 if a key is not found for
+ # at least one user ID
+ returnCode=0
+
+ # set the trap to remove any lockfiles on exit
+ trap "lockfile-remove $AUTHORIZED_KEYS" EXIT
# create a lockfile on authorized_keys
lockfile-create "$AUTHORIZED_KEYS"
for userID ; do
- log "processing user ID: $userID"
-
- process_user_id "$userID" | \
- while read -r ok keyid ; do
- sshKey=$(gpg2ssh "$keyid")
- # remove the old host key line
- remove_line "$AUTHORIZED_KEYS" "$sshKey"
- # if key OK, add new host line
- if [ "$ok" -eq '0' ] ; then
- ssh2authorized_keys "$userID" "$sshKey" >> "$AUTHORIZED_KEYS"
- fi
- done
+ # process the user ID, change return code if key not found for
+ # user ID
+ process_uid_authorized_keys "$userID" || returnCode=1
+
# touch the lockfile, for good measure.
lockfile-touch --oneshot "$AUTHORIZED_KEYS"
done
# remove the lockfile
lockfile-remove "$AUTHORIZED_KEYS"
-}
-# process known_hosts file
-# go through line-by-line, extract each host, and process with the
-# host processing function
-process_known_hosts() {
- local hosts
- local host
-
- # take all the hosts from the known_hosts file (first field),
- # grep out all the hashed hosts (lines starting with '|')...
- cat "$KNOWN_HOSTS" | meat | \
- cut -d ' ' -f 1 | grep -v '^|.*$' | \
- while IFS=, read -r -a hosts ; do
- process_hosts_known_hosts ${hosts[@]}
- done
+ return "$returnCode"
}
# process an authorized_user_ids file for authorized_keys
process_authorized_user_ids() {
local userid
+ local returnCode
+
+ # default return code is 0, and is set to 1 if a key for a user ID
+ # is not found
+ returnCode=0
authorizedUserIDs="$1"
- cat "$authorizedUserIDs" | meat | \
- while read -r userid ; do
- process_uids_authorized_keys "$userid"
+ # set the IFS to be newline for parsing the authorized_user_ids
+ # file. can't find it in BASH(1) (found it on the net), but it
+ # works.
+ IFS=$'\n'
+ for userid in $(cat "$authorizedUserIDs" | meat) ; do
+ update_authorized_keys "$userid" || returnCode=1
done
+
+ return "$returnCode"
}
# EXPERIMENTAL (unused) process userids found in authorized_keys file
process_authorized_keys() {
local authorizedKeys
local userID
+ local returnCode
+
+ # default return code is 0, and is set to 1 if a key for a user
+ # is not found
+ returnCode=0
authorizedKeys="$1"
# process the userid
log "processing userid: '$userID'"
- process_user_id "$userID" > /dev/null
+ process_user_id "$userID" > /dev/null || returnCode=1
done
+
+ return "$returnCode"
}
##################################################
void make_keyid_printable(printable_keyid out, gnutls_openpgp_keyid_t keyid)
{
assert(sizeof(out) >= 2*sizeof(keyid));
- hex_print_data((char*)out, (const char*)keyid, sizeof(keyid));
+ hex_print_data((char*)out, (const unsigned char*)keyid, sizeof(keyid));
}
/* you must have twice as many bytes in the out buffer as in the in buffer */
-void hex_print_data(char* out, const char* in, size_t incount)
+void hex_print_data(char* out, const unsigned char* in, size_t incount)
{
static const char hex[16] = "0123456789ABCDEF";
unsigned int inix = 0, outix = 0;
void collapse_printable_keyid(gnutls_openpgp_keyid_t out, printable_keyid in) {
unsigned int pkix = 0, outkix = 0;
-
while (pkix < sizeof(printable_keyid)) {
unsigned hi = hex2bin(in[pkix]);
unsigned lo = hex2bin(in[pkix + 1]);
}
}
+unsigned int hexstring2bin(unsigned char* out, const char* in) {
+ unsigned int pkix = 0, outkix = 0;
+ int hi = 0; /* which nybble is it? */
+
+ while (in[pkix]) {
+ unsigned char z = hex2bin(in[pkix]);
+ if (z != 0xff) {
+ if (!hi) {
+ if (out) out[outkix] = (z << 4);
+ hi = 1;
+ } else {
+ if (out) out[outkix] |= z;
+ hi = 0;
+ outkix++;
+ }
+ pkix++;
+ }
+ }
+ return outkix*8 + (hi ? 4 : 0);
+}
+
int convert_string_to_keyid(gnutls_openpgp_keyid_t out, const char* str) {
printable_keyid p;
int ret;
int convert_string_to_printable_keyid(printable_keyid out, const char* str);
/* you must have twice as many bytes in the out buffer as in the in buffer */
-void hex_print_data(char* out, const char* in, size_t incount);
+void hex_print_data(char* out, const unsigned char* in, size_t incount);
+
+/* expects a null-terminated string as in, containing an even number
+ of hexadecimal characters.
+
+ returns length in *bits* of raw data as output.
+
+ the out buffer must be at least half as long as in to hold the
+ output. if out is NULL, no output will be generated, but the
+ length will still be returned.
+*/
+unsigned int hexstring2bin(unsigned char* out, const char* in);
/* functions to get data into datum objects: */
/* FIXME: keyid should be const as well */
-int convert_private_pgp_to_x509(gnutls_x509_privkey_t* output, const gnutls_openpgp_privkey_t* pgp_privkey, gnutls_openpgp_keyid_t* keyid) {
+int convert_private_pgp_to_x509(gnutls_x509_privkey_t* output, const gnutls_openpgp_privkey_t* pgp_privkey, const unsigned char* keyfpr, unsigned int fprlen) {
gnutls_datum_t m, e, d, p, q, u, g, y, x;
gnutls_pk_algorithm_t pgp_algo;
unsigned int pgp_bits;
int ret;
- gnutls_openpgp_keyid_t curkeyid;
int subkeyidx;
int subkeycount;
int found = 0;
+ unsigned char fingerprint[20];
+ size_t fingerprint_length = sizeof(fingerprint);
init_datum(&m);
init_datum(&e);
return 1;
}
- if ((keyid == NULL) &&
+ if ((keyfpr == NULL) &&
(subkeycount > 0)) {
- err(0,"No keyid passed in, but there were %d keys to choose from\n", subkeycount + 1);
+ err(0,"No key identifier passed in, but there were %d keys to choose from\n", subkeycount + 1);
return 1;
}
- if (keyid != NULL) {
- ret = gnutls_openpgp_privkey_get_key_id(*pgp_privkey, curkeyid);
+ if (keyfpr != NULL) {
+ ret = gnutls_openpgp_privkey_get_fingerprint(*pgp_privkey, fingerprint, &fingerprint_length);
if (ret) {
- err(0,"Could not get keyid (error: %d)\n", ret);
+ err(0,"Could not get fingerprint (error: %d)\n", ret);
return 1;
}
+ if (fprlen > fingerprint_length) {
+ err(0, "Requested key identifier is longer than computed fingerprint\n");
+ return 1;
+ }
+ if (fingerprint_length > fprlen) {
+ err(0, "Only comparing last %d bits of key fingerprint\n", fprlen*8);
+ }
}
- if ((keyid == NULL) || (memcmp(*keyid, curkeyid, sizeof(gnutls_openpgp_keyid_t)) == 0)) {
+ if ((keyfpr == NULL) || (memcmp(fingerprint + (fingerprint_length - fprlen), keyfpr, fprlen) == 0)) {
/* we want to export the primary key: */
err(0,"exporting primary key\n");
} else {
/* lets trawl through the subkeys until we find the one we want: */
for (subkeyidx = 0; (subkeyidx < subkeycount) && !found; subkeyidx++) {
- ret = gnutls_openpgp_privkey_get_subkey_id(*pgp_privkey, subkeyidx, curkeyid);
+ ret = gnutls_openpgp_privkey_get_subkey_fingerprint(*pgp_privkey, subkeyidx, fingerprint, &fingerprint_length);
if (ret) {
- err(0,"Could not get keyid of subkey with index %d (error: %d)\n", subkeyidx, ret);
+ err(0,"Could not get fingerprint of subkey with index %d (error: %d)\n", subkeyidx, ret);
return 1;
}
- if (memcmp(*keyid, curkeyid, sizeof(gnutls_openpgp_keyid_t)) == 0) {
+ if (fprlen > fingerprint_length) {
+ err(0, "Requested key identifier is longer than computed fingerprint\n");
+ return 1;
+ }
+ if (fingerprint_length > fprlen) {
+ err(1, "Only comparing last %d bits of key fingerprint\n", fprlen*8);
+ }
+ if (memcmp(fingerprint + (fingerprint_length - fprlen), keyfpr, fprlen) == 0) {
err(0,"exporting subkey index %d\n", subkeyidx);
/* FIXME: this is almost identical to the block above for the
}
/* FIXME: keyid should be const also */
-int emit_public_openssh_from_pgp(const gnutls_openpgp_crt_t* pgp_crt, gnutls_openpgp_keyid_t* keyid) {
- gnutls_openpgp_keyid_t curkeyid;
+int emit_public_openssh_from_pgp(const gnutls_openpgp_crt_t* pgp_crt, const unsigned char* keyfpr, size_t fprlen) {
int ret;
int subkeyidx;
int subkeycount;
algorithm name: */
char output_data[20];
+ unsigned char fingerprint[20];
+ size_t fingerprint_length = sizeof(fingerprint);
+
/* variables for the output conversion: */
int pipestatus;
int pipefd, child_pid;
return 1;
}
- if ((keyid == NULL) &&
+ if ((keyfpr == NULL) &&
(subkeycount > 0)) {
- err(0,"No keyid passed in, but there were %d keys to choose from\n", subkeycount + 1);
+ err(0,"No key identifier passed in, but there were %d keys to choose from\n", subkeycount + 1);
return 1;
}
- if (keyid != NULL) {
- ret = gnutls_openpgp_crt_get_key_id(*pgp_crt, curkeyid);
+ if (keyfpr != NULL) {
+ ret = gnutls_openpgp_crt_get_fingerprint(*pgp_crt, fingerprint, &fingerprint_length);
if (ret) {
- err(0,"Could not get keyid (error: %d)\n", ret);
+ err(0,"Could not get key fingerprint (error: %d)\n", ret);
return 1;
}
+ if (fprlen > fingerprint_length) {
+ err(0, "Requested key identifier is longer than computed fingerprint\n");
+ return 1;
+ }
+ if (fingerprint_length > fprlen) {
+ err(0, "Only comparing last %d bits of key fingerprint\n", fprlen*8);
+ }
}
- if ((keyid == NULL) || (memcmp(*keyid, curkeyid, sizeof(gnutls_openpgp_keyid_t)) == 0)) {
+ if ((keyfpr == NULL) || (memcmp(fingerprint + (fingerprint_length - fprlen), keyfpr, fprlen) == 0)) {
/* we want to export the primary key: */
err(0,"exporting primary key\n");
} else {
/* lets trawl through the subkeys until we find the one we want: */
for (subkeyidx = 0; (subkeyidx < subkeycount) && !found; subkeyidx++) {
- ret = gnutls_openpgp_crt_get_subkey_id(*pgp_crt, subkeyidx, curkeyid);
+ ret = gnutls_openpgp_crt_get_subkey_fingerprint(*pgp_crt, subkeyidx, fingerprint, &fingerprint_length);
if (ret) {
- err(0,"Could not get keyid of subkey with index %d (error: %d)\n", subkeyidx, ret);
+ err(0,"Could not get fingerprint of subkey with index %d (error: %d)\n", subkeyidx, ret);
return 1;
}
- if (memcmp(*keyid, curkeyid, sizeof(gnutls_openpgp_keyid_t)) == 0) {
+ if (fprlen > fingerprint_length) {
+ err(0, "Requested key identifier is longer than computed fingerprint\n");
+ return 1;
+ }
+ if (fingerprint_length > fprlen) {
+ err(1, "Only comparing last %d bits of key fingerprint\n", fprlen*8);
+ }
+ if (memcmp(fingerprint + (fingerprint_length - fprlen), keyfpr, fprlen) == 0) {
err(0,"exporting subkey index %d\n", subkeyidx);
/* FIXME: this is almost identical to the block above for the
int main(int argc, char* argv[]) {
gnutls_datum_t data;
- int ret;
+ int ret = 0;
gnutls_x509_privkey_t x509_privkey;
gnutls_openpgp_privkey_t pgp_privkey;
gnutls_openpgp_crt_t pgp_crt;
char output_data[10240];
size_t ods = sizeof(output_data);
- gnutls_openpgp_keyid_t keyid;
- gnutls_openpgp_keyid_t* use_keyid;
+ unsigned char * fingerprint = NULL;
+ size_t fpr_size;
+ char * prettyfpr = NULL;
init_gnutls();
- /* figure out what keyid we should be looking for: */
- use_keyid = NULL;
+ /* figure out what key we should be looking for: */
if (argv[1] != NULL) {
- ret = convert_string_to_keyid(keyid, argv[1]);
- if (ret != 0)
- return ret;
- use_keyid = &keyid;
+ if (strlen(argv[1]) > 81) {
+ /* safety check to avoid some sort of wacky overflow situation:
+ there's no reason that the key id should be longer than twice
+ a sane fingerprint (one byte between chars, and then another
+ two at the beginning and end) */
+ err(0, "Key identifier is way too long. Please use at most 40 hex digits.\n");
+ return 1;
+ }
+
+ fpr_size = hexstring2bin(NULL, argv[1]);
+ if (fpr_size > 40*4) {
+ err(0, "Key identifier is longer than 40 hex digits\n");
+ return 1;
+ }
+ /* since fpr_size is initially in bits: */
+ if (fpr_size % 8 != 0) {
+ err(0, "Please provide an even number of hex digits for the key identifier\n");
+ return 1;
+ }
+ fpr_size /= 8;
+
+ fingerprint = malloc(sizeof(unsigned char) * fpr_size);
+ bzero(fingerprint, sizeof(unsigned char) * fpr_size);
+ hexstring2bin(fingerprint, argv[1]);
+
+ prettyfpr = malloc(sizeof(unsigned char)*fpr_size*2 + 1);
+ if (prettyfpr != NULL) {
+ hex_print_data(prettyfpr, fingerprint, fpr_size);
+ prettyfpr[sizeof(unsigned char)*fpr_size*2] = '\0';
+ err(1, "searching for key with fingerprint '%s'\n", prettyfpr);
+ free(prettyfpr);
+ }
+
+ if (fpr_size < 4) {
+ err(0, "You MUST provide at least 8 hex digits in any key identifier\n");
+ return 1;
+ }
+ if (fpr_size < 8)
+ err(0, "You should provide at least 16 hex digits in any key identifier (proceeding with %d digits anyway)\n", fpr_size*2);
+
}
return 1;
}
- ret = convert_private_pgp_to_x509(&x509_privkey, &pgp_privkey, use_keyid);
+ ret = convert_private_pgp_to_x509(&x509_privkey, &pgp_privkey, fingerprint, fpr_size);
gnutls_openpgp_privkey_deinit(pgp_privkey);
if (ret)
/* we're dealing with a public key */
err(0,"Translating public key\n");
- ret = emit_public_openssh_from_pgp(&pgp_crt, use_keyid);
+ ret = emit_public_openssh_from_pgp(&pgp_crt, fingerprint, fpr_size);
} else {
/* we have no idea what kind of key this is at all anyway! */
}
gnutls_global_deinit();
+ free(fingerprint);
return 0;
}
# unset some environment variables that could screw things up
GREP_OPTIONS=
+# default return code
+ERR=0
+
########################################################################
# FUNCTIONS
########################################################################
[ -e "$MS_CONF" ] && . "$MS_CONF"
# set empty config variable with defaults
-AUTHORIZED_USER_IDS=${AUTHORIZED_USER_IDS:-"${MS_HOME}/authorized_user_ids"}
GNUPGHOME=${GNUPGHOME:-"${HOME}/.gnupg"}
KEYSERVER=${KEYSERVER:-"subkeys.pgp.net"}
CHECK_KEYSERVER=${CHECK_KEYSERVER:="true"}
-REQUIRED_HOST_KEY_CAPABILITY=${REQUIRED_HOST_KEY_CAPABILITY:-"a"}
-REQUIRED_USER_KEY_CAPABILITY=${REQUIRED_USER_KEY_CAPABILITY:-"a"}
KNOWN_HOSTS=${KNOWN_HOSTS:-"${HOME}/.ssh/known_hosts"}
-AUTHORIZED_KEYS=${AUTHORIZED_KEYS:-"${HOME}/.ssh/authorized_keys"}
HASH_KNOWN_HOSTS=${HASH_KNOWN_HOSTS:-"true"}
+AUTHORIZED_KEYS=${AUTHORIZED_KEYS:-"${HOME}/.ssh/authorized_keys"}
+
+# other variables
+AUTHORIZED_USER_IDS=${AUTHORIZED_USER_IDS:-"${MS_HOME}/authorized_user_ids"}
+REQUIRED_HOST_KEY_CAPABILITY=${REQUIRED_HOST_KEY_CAPABILITY:-"a"}
+REQUIRED_USER_KEY_CAPABILITY=${REQUIRED_USER_KEY_CAPABILITY:-"a"}
export GNUPGHOME
# make sure the user monkeysphere home directory exists
mkdir -p -m 0700 "$MS_HOME"
touch "$AUTHORIZED_USER_IDS"
-touch "$AUTHORIZED_KEYS"
case $COMMAND in
'update-known_hosts'|'update-known-hosts'|'k')
# if hosts are specified on the command line, process just
# those hosts
if [ "$1" ] ; then
- process_hosts_known_hosts "$@"
+ update_known_hosts "$@" || ERR=1
# otherwise, if no hosts are specified, process every host
# in the user's known_hosts file
failure "known_hosts file '$KNOWN_HOSTS' is empty."
fi
log "processing known_hosts file..."
- process_known_hosts
+ process_known_hosts || ERR=1
fi
log "known_hosts file updated."
# process authorized_user_ids file
log "processing authorized_user_ids file..."
- process_authorized_user_ids "$AUTHORIZED_USER_IDS"
+ process_authorized_user_ids "$AUTHORIZED_USER_IDS" || ERR=1
log "authorized_keys file updated."
;;
Type '$PGRM help' for usage."
;;
esac
+
+exit "$ERR"
########################################################################
PGRM=$(basename $0)
-SHAREDIR=${SHAREDIR:-"/usr/share/monkeysphere"}
-export SHAREDIR
-. "${SHAREDIR}/common"
+SHARE=${SHARE:-"/usr/share/monkeysphere"}
+export SHARE
+. "${SHARE}/common"
+
+VARLIB="/var/lib/monkeysphere"
+export VARLIB
# date in UTF format if needed
DATE=$(date -u '+%FT%T')
# unset some environment variables that could screw things up
GREP_OPTIONS=
+# default return code
+ERR=0
+
########################################################################
# FUNCTIONS
########################################################################
MonkeySphere server admin tool.
subcommands:
- update-users (s) [USER]... update users authorized_keys files
+ update-users (u) [USER]... update users authorized_keys files
gen-key (g) [HOSTNAME] generate gpg key for the server
show-fingerprint (f) show server's host key fingerprint
publish-key (p) publish server's host key to keyserver
local hostName
hostName=${1:-$(hostname --fqdn)}
- service=${SERVICE:-"ssh"}
- userID="${service}://${hostName}"
+
+ SERVICE=${SERVICE:-"ssh"}
+ userID="${SERVICE}://${hostName}"
if gpg --list-key ="$userID" > /dev/null 2>&1 ; then
failure "Key for '$userID' already exists"
# write the key to the file
# NOTE: assumes that the primary key is the proper key to use
- (umask 077 && gpgsecret2ssh "$keyID" > "${MS_HOME}/ssh_host_rsa_key")
- log "Private SSH host key output to file: ${MS_HOME}/ssh_host_rsa_key"
+ (umask 077 && gpgsecret2ssh "$keyID" > "${VARLIB}/ssh_host_rsa_key")
+ log "Private SSH host key output to file: ${VARLIB}/ssh_host_rsa_key"
}
# gpg output key fingerprint
[ -e "$MS_CONF" ] && . "$MS_CONF"
# set empty config variable with defaults
-GNUPGHOME=${GNUPGHOME:-"${MS_HOME}/gnupg"}
+MONKEYSPHERE_USER=${MONKEYSPHERE_USER:-"monkeysphere"}
KEYSERVER=${KEYSERVER:-"subkeys.pgp.net"}
CHECK_KEYSERVER=${CHECK_KEYSERVER:="true"}
-REQUIRED_USER_KEY_CAPABILITY=${REQUIRED_USER_KEY_CAPABILITY:-"a"}
AUTHORIZED_USER_IDS=${AUTHORIZED_USER_IDS:-"%h/.config/monkeysphere/authorized_user_ids"}
-USER_CONTROLLED_AUTHORIZED_KEYS=${USER_CONTROLLED_AUTHORIZED_KEYS:-"%h/.ssh/authorized_keys"}
+RAW_AUTHORIZED_KEYS=${RAW_AUTHORIZED_KEYS:-"%h/.ssh/authorized_keys"}
-export GNUPGHOME
+# other variables
+REQUIRED_USER_KEY_CAPABILITY=${REQUIRED_USER_KEY_CAPABILITY:-"a"}
+GNUPGHOME_HOST=${GNUPGHOME_HOST:-"${VARLIB}/gnupg-host"}
+GNUPGHOME_AUTHENTICATION=${GNUPGHOME_AUTHENTICATION:-"${VARLIB}/gnupg-authentication"}
-# make sure the monkeysphere home directory exists
-mkdir -p "${MS_HOME}/authorized_user_ids"
-# make sure gpg home exists with proper permissions
+# set default GNUPGHOME, and make sure the directory exists. this is
+# true for all functions expect user authentication
+# (ie. update-users).
+GNUPGHOME="$GNUPGHOME_HOST"
+export GNUPGHOME
mkdir -p -m 0700 "$GNUPGHOME"
-# make sure the authorized_keys directory exists
-mkdir -p "${CACHE}/authorized_keys"
case $COMMAND in
- 'update-users'|'update-user'|'s')
+ 'update-users'|'update-user'|'u')
if [ "$1" ] ; then
# get users from command line
unames="$@"
unames=$(getent passwd | cut -d: -f1)
fi
+ # set mode
+ MODE="authorized_keys"
+
+ # make sure the authorized_keys directory exists
+ mkdir -p "${VARLIB}/authorized_keys"
+
+ # set GNUPGHOME, and make sure the directory exists
+ GNUPGHOME="$GNUPGHOME_AUTHENTICATION"
+ export GNUPGHOME
+ mkdir -p -m 0700 "$GNUPGHOME"
+
# loop over users
for uname in $unames ; do
- MODE="authorized_keys"
-
# check all specified users exist
if ! getent passwd "$uname" >/dev/null ; then
error "----- unknown user '$uname' -----"
continue
fi
- # set authorized_user_ids variable,
- # translate ssh-style path variables
+ # set authorized_user_ids and raw authorized_keys variables,
+ # translating ssh-style path variables
authorizedUserIDs=$(translate_ssh_variables "$uname" "$AUTHORIZED_USER_IDS")
+ rawAuthorizedKeys=$(translate_ssh_variables "$uname" "$RAW_AUTHORIZED_KEYS")
- # skip user if authorized_user_ids file does not exist
- if [ ! -f "$authorizedUserIDs" ] ; then
+ # if neither is found, skip user
+ if [ ! -s "$authorizedUserIDs" -a ! -s "$rawAuthorizedKeys" ] ; then
continue
fi
# temporary authorized_keys file
AUTHORIZED_KEYS=$(mktemp)
- # skip if the user's authorized_user_ids file is empty
- if [ ! -s "$authorizedUserIDs" ] ; then
- log "authorized_user_ids file '$authorizedUserIDs' is empty."
- continue
- fi
+ # trap to delete file on exit
+ trap "rm -f $AUTHORIZE_KEYS" EXIT
# process authorized_user_ids file
- log "processing authorized_user_ids file..."
- process_authorized_user_ids "$authorizedUserIDs"
+ if [ -s "$authorizedUserIDs" ] ; then
+ log "processing authorized_user_ids file..."
+ process_authorized_user_ids "$authorizedUserIDs"
+ fi
# add user-controlled authorized_keys file path if specified
- if [ "$USER_CONTROLLED_AUTHORIZED_KEYS" != '-' ] ; then
- userAuthorizedKeys=$(translate_ssh_variables "$uname" "$USER_CONTROLLED_AUTHORIZED_KEYS")
- if [ -f "$userAuthorizedKeys" ] ; then
- log -n "adding user's authorized_keys file... "
- cat "$userAuthorizedKeys" >> "$AUTHORIZED_KEYS"
+ if [ "$RAW_AUTHORIZED_KEYS" != '-' ] ; then
+ if [ -s "$rawAuthorizedKeys" ] ; then
+ log -n "adding raw authorized_keys file... "
+ cat "$rawAuthorizedKeys" >> "$AUTHORIZED_KEYS"
loge "done."
fi
fi
- # move the temp authorized_keys file into place
- mv -f "$AUTHORIZED_KEYS" "${CACHE}/authorized_keys/${uname}"
+ # if the resulting authorized_keys file is not empty, move
+ # the temp authorized_keys file into place
+ if [ -s "$AUTHORIZED_KEYS" ] ; then
+ # openssh appears to check the contents of the
+ # authorized_keys file as the user in question, so the
+ # file must be readable by that user at least.
+ # FIXME: is there a better way to do this?
+ chgrp $(getent passwd "$uname" | cut -f4 -d:) "$AUTHORIZED_KEYS"
+ chmod g+r "$AUTHORIZED_KEYS"
+
+ mv -f "$AUTHORIZED_KEYS" "${VARLIB}/authorized_keys/${uname}"
- log "authorized_keys file updated."
+ log "authorized_keys file updated."
+
+ # else destroy it
+ else
+ rm -f "$AUTHORIZED_KEYS"
+ fi
done
;;
HOST="$1"
PORT="$2"
+MS_HOME=${MS_HOME:-"${HOME}/.config/monkeysphere"}
+
if [ -z "$HOST" ] ; then
log "host must be specified."
usage
# FIXME: this only works for default known_hosts location
hostKey=$(ssh-keygen -F "$HOST")
if [ "$hostKey" ] ; then
- # if the check keyserver variable is NOT set to true...
- if [ "$CHECK_KEYSERVER" != 'true' ] ; then
- # schedule a keyserver check for host at a later time
- echo "monkeysphere update-known_hosts $HOST" | at noon
- fi
+ # do not check the keyserver
+ # FIXME: more nuanced checking should be done here to properly
+ # take into consideration hosts that join monkeysphere by
+ # converting an existing and known ssh key
+ CHECK_KEYSERVER="false"
+
# if the host key is not found in the known_hosts file...
else
# check the keyserver