summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Makefile3
-rw-r--r--doc/MonkeySpec105
-rw-r--r--doc/README5
-rw-r--r--doc/git init128
-rw-r--r--gnutls-helpers.c17
-rw-r--r--gnutls-helpers.h8
-rw-r--r--gpg2ssh.c2
-rw-r--r--ssh2gpg.c171
9 files changed, 438 insertions, 2 deletions
diff --git a/.gitignore b/.gitignore
index ae9fbd8..80bf65d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,4 @@
*.[ao]
monkeysphere
gpg2ssh
+ssh2gpg
diff --git a/Makefile b/Makefile
index 4fb4556..aa18aaa 100644
--- a/Makefile
+++ b/Makefile
@@ -4,6 +4,9 @@ monkeysphere: main.c gnutls-helpers.o
gpg2ssh: gpg2ssh.c gnutls-helpers.o
gcc -g -Wall --pedantic -o gpg2ssh gpg2ssh.c `libgnutls-config --libs --cflags` -lgnutls-extra gnutls-helpers.o
+ssh2gpg: ssh2gpg.c gnutls-helpers.o
+ gcc -g -Wall --pedantic -o ssh2gpg ssh2gpg.c `libgnutls-config --libs --cflags` -lgnutls-extra gnutls-helpers.o
+
%.o: %.c
gcc -g -Wall --pedantic -o $@ -c $<
diff --git a/doc/MonkeySpec b/doc/MonkeySpec
new file mode 100644
index 0000000..7a19df0
--- /dev/null
+++ b/doc/MonkeySpec
@@ -0,0 +1,105 @@
+THE MONKEYSPHERE
+================
+
+AGENDA
+======
+[x] clowning
+[ ] work
+[x] jrollins will talk and gesture - in progress
+
+COMPONENTS
+==========
+* client-side componants
+** "Marmoset": update known_hosts file with public key of server(s):
+*** be responsible for removing keys from the file as key revocation happens
+*** be responsible for updating a key in the file where there is a key replacement
+*** must result in a file that is parsable by the existing ssh client without errors
+*** manual management must be allowed without stomping on it
+*** provide a simple, intelligible, clear policy for key acceptance
+*** questions: should this query keyserver & update known host files? (we already
+ have awesome tool that queries keyservers and updates a web of trust (gpg)
+** "Howler": simple script that could be placed as a trigger function (in your .ssh/config)
+*** runs on connection to a certain host
+*** triggers update to known_hosts file then makes connection
+*** proxy-command | pre-hook script | wrapper script
+** "Langur": policy-editor for viewing/editing policies
+
+* server-side componants
+** "Rhesus" updates a per-user authorized_keys file, instead of updating a
+ known_hosts file from a public key by matching a specified user-id (for given
+ user: update authkeys file with public keys derived from authorized_uids
+ file)
+*** Needs to operate with the same principles that Marmoset client-side does
+** "Tamarin" triggers Rhesus during an attempt to initiate a connection or a scheduler (or both)
+** "Barbary" - policy editor / viewer
+
+* common componants
+** Create a ssh keypair from a openpgp keypair
+
+from ssh_config(5):
+ LocalCommand
+ Specifies a command to execute on the local machine after suc‐
+ cessfully connecting to the server. The command string extends
+ to the end of the line, and is executed with /bin/sh. This
+ directive is ignored unless PermitLocalCommand has been enabled.
+
+
+NOTES
+=====
+* Daniel and Elliot lie. <check>
+* We will use a distributed VCS, each developer will create their own git repository and publish it publically for others to pull from, mail out
+* public project page doesn't perhaps make sense yet
+* approximate goal - using the web of trust to authenticate ppl for SSH
+* outline of various components of monkeysphere
+* M: what does it mean to be in the monkeysphere? not necessarily a great coder.
+* J: interested in seeing project happen, not in actually doing it. anybody can contribute as much as they want.
+* J: if we put the structure in place to work on monkeysphere then we don't have to do anything
+* D: we are not creating
+* understand gpg's keyring better, understanding tools better, building scripts
+* Some debian packages allow automated configuration of config files.
+
+
+* GENERAL GOAL - use openpgp web-of-trust to authenticate ppl for SSH
+* SPECIFIC GOAL - allow openssh to tie into pgp web-of-trust without modifying either openpgp and openssh
+* DESIGN GOALS - authentication, use the existing generic OpenSSH client, the admin can make it default, although end-user should be decide to use monkeysphere or not
+* DESIGN GOAL - use of monkeysphere should not radically change connecting-to-server experience
+* GOAL - pick a monkey-related name for each component
+
+Dramatis Personae: http://en.wikipedia.org/wiki/Alice_and_Bob
+Backstory: http://www.conceptlabs.co.uk/alicebob.html
+
+* Use Case: Bob wants to sign on to the computer "mangabey" via monkeysphere
+ framework. He doesn't have access to the machine, but he knows Alice, who is
+ the admin of magabey. Alice creates a user bob and puts bob's userid in the
+ auth_user_ids file for bob. Tamarin triggers which causes Rhesus to take all
+ the things in the auth_userids file, takes those users, look son a keyserver
+ finds the public keys for the users, converts the gpg public keys into ssh
+ public keys and inserts those into a user_authorized_keys file. Bob goes to
+ connect, bob's ssh client which is monkeysphere enbaled, howler is triggered
+ which triggers marmoset which looks out into the web of trust and find an
+ OpenPGP key that has a userid that matches the URI of magabey. Marmoset checks
+ to see if this key for mangabey has been signed by any keys that you trust
+ (based on your policy). Has this key been signed by somebody that you trust?
+ If yes, connect, if no: abort or fail-through or whatever. Alice has signed
+ this uid, so Marmoset says "OK, this server has been verified" it then
+ converts the gpg public key into a ssh public key and then adds this gpg key
+ to the known_host file. ssh says, "you" are about to connect to magabey and
+ you know this is magabey because alice says so and you trust alice". The gpg
+ private key of bob has to be converted (somehow, via agent or something) into
+ a ssh private_key. SSH connection happens.
+
+Host identity piece of monkeysphere could be used without buying into the
+authorization component.
+
+Monkeysphere is authentication layer that allows the sysadmin to perform
+authorization on user identities instead of on keys, it additionally allows the
+sysadmin also to authenticate the server to the end-user.
+
+git clone http://git.mlcastle.net/monkeysphere.git/ monkeysphere
+
+Fix gpgkey2ssh so that the entire key fingerprint will work, accept full fingerprint, or accept a pipe and do the conversion
+Write manpage for gpgkey2ssh
+gpg private key (start with passwordless) to PEM encoded private key: perl libraries, libopencdk / gnutls, gpgme
+setup remote git repo
+think through / plan merging of known_hosts (& auth_keys?)
+think about policies and their representation \ No newline at end of file
diff --git a/doc/README b/doc/README
new file mode 100644
index 0000000..4c70d1d
--- /dev/null
+++ b/doc/README
@@ -0,0 +1,5 @@
+ Monkeysphere
+ ------------
+
+
+This is the README!
diff --git a/doc/git init b/doc/git init
new file mode 100644
index 0000000..7ba5071
--- /dev/null
+++ b/doc/git init
@@ -0,0 +1,128 @@
+remote$ mkdir public_html/git
+(etch)
+remote$ GIT_DIR=~/public_html/git/monkeysphere.git git init-db
+remote$ cd ~/public_html/git/monkeysphere.git
+remote$ chmod a+x hooks/post-update
+# NOT SURE IF THIS IS NEEDED: remote$ git-update-server-info
+fetch = +refs/heads/*:refs/remotes/dkg/*
+
+(newer)
+remote$ mkdir -p public_html/git/monkey.git
+remote$ cd public_html/git/monkey.git
+remote$ git --bare init
+remote$ chmod a+x hooks/post-update
+remote$ git-update-server-info
+
+(new way! no origin/)
+$ cd ~/src
+$ mkdir monkeysphere
+$ cd monkeysphere
+$ git init
+$ git remote add -f mlcastle http://git.mlcastle.net/monkeysphere.git/
+$ git remote add grunt grunt:/whatever
+$ git config remote.grunt.push "+refs/heads/*"
+$ git merge mlcastle/master
+$ git push grunt
+
+(old way!)
+(in ~/src or wherever)
+local$ git clone http://git.mlcastle.net/monkeysphere.git/ monkeysphere
+local$ cd monkeysphere
+
+.git/config:
+
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+
+## THIS ONE NEEDS TO BE CHANGED TO YOUR REMOTE URI
+[remote "post"]
+ url = YOUR-REMOTE-URL/git/monkeysphere.git
+ push = +refs/heads/*
+### THE ABOVE ONE NEEDS TO BE CHANGED
+
+[remote "mlcastle"]
+ url = http://git.mlcastle.net/monkeysphere.git/
+ fetch = +refs/heads/*:refs/remotes/mlcastle/*
+
+[remote "jrollins"]
+ url = http://lair.fifthhorseman.net/~jrollins/git/monkeysphere.git/
+ fetch = +refs/heads/*:refs/remotes/jrollins/*
+
+[remote "dkg"]
+ url = http://lair.fifthhorseman.net/~dkg/git/monkeysphere.git/
+ fetch = +refs/heads/*:refs/remotes/dkg/*
+
+[remote "mjgoins"] SEE: dkg, jrollins, etc.
+
+[remote "micah"]
+ url = http://micah.riseup.net/git/monkeysphere.git
+ fetch = +refs/heads/*:refs/remotes/micah/*
+
+[remote "enw"]
+ url = http://lair.fifthhorseman.net/~enw/git/monkeysphere.git/
+ fetch = +refs/heads/*:refs/remotes/enw/*
+
+[remote "rossg"]
+ url = http://lair.fifthhorseman.net/~rossg/git/monkeysphere.git/
+ fetch = +refs/heads/*:refs/remotes/rossg/*
+
+[remote "greg"]
+ url = http://lair.fifthhorseman.net/~greg/git/monkeysphere.git/
+ fetch = +refs/heads/*:refs/remotes/greg/*
+ blood type =
+
+-----------------
+[remote "upload"]
+ url = ssh://z.mlcastle.net/var/www/git/monkeysphere.git/
+ push = +refs/heads/*
+
+
+$ git fetch dkg
+$ git checkout master
+$ git merge remotes/dkg/master
+$ git push post
+
+
+
+
+
+
+
+grunt's fingerprint: be:43:9c:03:9c:04:1a:97:7a:61:8a:fe:71:9d:6c:67
+(grunt is lair.fifthhorseman.net)
+
+for foo in $(git remote); do git fetch $foo; done
+
+
+
+set mainfont {Arial 12}
+set textfont { Courier 12}
+set uifont {Arial 10 bold}
+set tabstop 8
+set findmergefiles 0
+set maxgraphpct 50
+set maxwidth 16
+set cmitmode patch
+set wrapcomment none
+set showneartags 1
+set showlocalchanges 1
+set datetimeformat {%Y-%m-%d %H:%M:%S}
+set limitdiffs 1
+set bgcolor white
+set fgcolor black
+set colors {green red blue magenta darkgrey brown orange}
+set diffcolors {red "#00a000" blue}
+set diffcontext 3
+set selectbgcolor gray85
+set geometry(main) 1280x936+14+28
+set geometry(topwidth) 1278
+set geometry(topheight) 286
+set geometry(pwsash0) "638 1"
+set geometry(pwsash1) "903 1"
+set geometry(botwidth) 1001
+set geometry(botheight) 638
+set permviews {}
+
diff --git a/gnutls-helpers.c b/gnutls-helpers.c
index 5a567e2..6eae29e 100644
--- a/gnutls-helpers.c
+++ b/gnutls-helpers.c
@@ -345,3 +345,20 @@ int validate_ssh_host_userid(const char* userid) {
setlocale(LC_ALL, oldlocale);
return 1;
}
+
+/* http://tools.ietf.org/html/rfc4880#section-5.5.2 */
+size_t get_openpgp_mpi_size(gnutls_datum_t* d) {
+ return 2 + d->size;
+}
+
+int write_openpgp_mpi_to_fd(int fd, gnutls_datum_t* d) {
+ uint16_t x;
+
+ x = d->size * 8;
+ x = htons(x);
+
+ write(fd, &x, sizeof(x));
+ write(fd, d->data, d->size);
+
+ return 0;
+}
diff --git a/gnutls-helpers.h b/gnutls-helpers.h
index 398413f..9ea22a3 100644
--- a/gnutls-helpers.h
+++ b/gnutls-helpers.h
@@ -54,7 +54,7 @@ int set_datum_string(gnutls_datum_t* d, const char* s);
datum */
int set_datum_fd(gnutls_datum_t* d, int fd);
-/* read the file indicated (by na1me) in the fname parameter. store
+/* read the file indicated (by name) in the fname parameter. store
its entire contents in a single datum. */
int set_datum_file(gnutls_datum_t* d, const char* fname);
@@ -64,3 +64,9 @@ int create_writing_pipe(pid_t* pid, const char* path, char* const argv[]);
/* return 0 if userid matches the monkeysphere spec for ssh host user IDs */
int validate_ssh_host_userid(const char* userid);
+
+/* how many bytes will it take to write out this datum in OpenPGP MPI form? */
+size_t get_openpgp_mpi_size(gnutls_datum_t* d);
+
+/* write the MPI stored in gnutls_datum_t to file descriptor fd: */
+int write_openpgp_mpi_to_fd(int fd, gnutls_datum_t* d);
diff --git a/gpg2ssh.c b/gpg2ssh.c
index 6155549..a1e94df 100644
--- a/gpg2ssh.c
+++ b/gpg2ssh.c
@@ -146,7 +146,7 @@ int main(int argc, char* argv[]) {
} else {
err("primary key is only good for: 0x%08x. Trying subkeys...\n", usage);
- if (ret = gnutls_openpgp_crt_get_auth_subkey(openpgp_crt, keyid), ret) {
+ if (ret = gnutls_openpgp_crt_get_auth_subkey(openpgp_crt, keyid, 0), ret) {
err("failed to find a subkey capable of authentication (error: %d)\n", ret);
return ret;
}
diff --git a/ssh2gpg.c b/ssh2gpg.c
new file mode 100644
index 0000000..b14a540
--- /dev/null
+++ b/ssh2gpg.c
@@ -0,0 +1,171 @@
+#include "gnutls-helpers.h"
+
+#include <gnutls/openpgp.h>
+#include <gnutls/x509.h>
+
+/* for waitpid() */
+#include <sys/types.h>
+#include <sys/wait.h>
+
+/* for time() */
+#include <time.h>
+
+/* for htons() */
+#include <arpa/inet.h>
+
+
+/*
+ Author: Daniel Kahn Gillmor <dkg@fifthhorseman.net>
+ Date: Sun, 2008-04-20
+ License: GPL v3 or later
+
+ monkeysphere public key translator: execute this with an ssh
+ private key on stdin. It currently only works with RSA keys.
+
+ it should eventually work with OpenSSH-style public keys instead of
+ the full private key, but it was easier to do this way.
+
+ It shoud spit out a version of the public key suitable for acting
+ as an OpenPGP public sub key packet.
+
+ */
+
+int main(int argc, char* argv[]) {
+ gnutls_datum_t data;
+ int ret;
+ gnutls_x509_privkey_t x509_privkey;
+ gnutls_openpgp_crt_t openpgp_crt;
+ gnutls_openpgp_keyid_t keyid;
+ printable_keyid p_keyid;
+ unsigned int keyidx;
+ unsigned int usage, bits;
+ gnutls_pk_algorithm_t algo;
+
+ unsigned char packettag;
+ unsigned char openpgpversion;
+ time_t timestamp;
+ uint32_t clunkytime;
+ unsigned char openpgpalgo;
+ unsigned int packetlen;
+ uint16_t plen;
+
+ gnutls_datum_t m, e, d, p, q, u, g, y;
+ gnutls_datum_t algolabel;
+
+ char output_data[10240];
+ char userid[10240];
+ size_t uidsz = sizeof(userid);
+
+ const gnutls_datum_t* all[5];
+ int pipefd;
+ pid_t child_pid;
+ char* const args[] = {"/usr/bin/base64", "--wrap=0", NULL};
+ const char* algoname;
+ int mpicount;
+ int pipestatus;
+
+ init_gnutls();
+
+ init_datum(&data);
+
+ init_datum(&m);
+ init_datum(&e);
+ init_datum(&d);
+ init_datum(&p);
+ init_datum(&q);
+ init_datum(&u);
+ init_datum(&g);
+ init_datum(&y);
+
+ init_datum(&algolabel);
+
+ init_keyid(keyid);
+
+ /* slurp in the private key from stdin */
+ if (ret = set_datum_fd(&data, 0), ret) {
+ err("didn't read file descriptor 0\n");
+ return 1;
+ }
+
+
+ if (ret = gnutls_x509_privkey_init(&x509_privkey), ret) {
+ err("Failed to initialize private key structure (error: %d)\n", ret);
+ return 1;
+ }
+
+ err("assuming PEM formatted private key\n");
+ if (ret = gnutls_x509_privkey_import(x509_privkey, &data, GNUTLS_X509_FMT_PEM), ret) {
+ err("failed to import the PEM-encoded private key (error: %d)\n", ret);
+ return ret;
+ }
+
+ algo = gnutls_x509_privkey_get_pk_algorithm(x509_privkey);
+ if (algo < 0) {
+ err("failed to get the algorithm of the PEM-encoded public key (error: %d)\n", algo);
+ return algo;
+ } else if (algo == GNUTLS_PK_RSA) {
+ err("RSA private key\n");
+ ret = gnutls_x509_privkey_export_rsa_raw(x509_privkey, &m, &e, &d, &p, &q, &u);
+ if (GNUTLS_E_SUCCESS != ret) {
+ err ("failed to export RSA key parameters (error: %d)\n", ret);
+ return 1;
+ }
+ err("Modulus size %d, exponent size %d\n", m.size, e.size);
+ } else if (algo == GNUTLS_PK_DSA) {
+ err("DSA Key, not implemented!!\n", bits);
+ return 1;
+ } else {
+ err("Key was not RSA or DSA -- can't deal! (actual algorithm was: %d)\n", algo);
+ return 1;
+ }
+
+ /* now we have algo, and the various MPI data are set. Can we
+ export them as a public subkey packet? */
+
+ /* this packet should be tagged 14, and should contain:
+
+ 1 octet: version (4)
+ 4 octets: time of generation (seconds since 1970)
+ 1 octet: algo (http://tools.ietf.org/html/rfc4880#section-5.5.2 implies 1 for RSA)
+
+ MPI: modulus
+ MPI: exponent
+ */
+
+ packetlen = 1 + 4 + 1;
+ /* FIXME: this is RSA only. for DSA, there'll be more: */
+ packetlen += get_openpgp_mpi_size(&m) + get_openpgp_mpi_size(&e);
+
+ /* FIXME: we should generate this bound more cleanly -- i just
+ happen to know that 65535 is 2^16-1: */
+ if (packetlen > 65535) {
+ err("packet length is too long (%d)\n", packetlen);
+ return 1;
+ }
+
+ /* we're going to emit an old-style packet, with tag 14 (public
+ subkey), with a two-octet packet length */
+ packettag = 0x80 | (14 << 2) | 1;
+
+ write(1, &packettag, sizeof(packettag));
+ plen = htons(packetlen);
+ write(1, &plen, sizeof(plen));
+
+ openpgpversion = 4;
+ write(1, &openpgpversion, 1);
+
+ timestamp = time(NULL);
+ clunkytime = htonl(timestamp);
+ write(1, &clunkytime, 4);
+
+ /* FIXME: handle things other than RSA */
+ openpgpalgo = 1;
+ write(1, &openpgpalgo, 1);
+
+ write_openpgp_mpi_to_fd(1, &m);
+ write_openpgp_mpi_to_fd(1, &e);
+
+ gnutls_x509_privkey_deinit(x509_privkey);
+ gnutls_global_deinit();
+ return 0;
+}