Commit afc0eb2c authored by Nick Mathewson's avatar Nick Mathewson 🎨
Browse files

Tested backends for directory signing and checking. Directory parser...

Tested backends for directory signing and checking.  Directory parser completely refactored.  Need documentation and integration.  Explanitory mail forthcoming.


svn:r271
parent bbddd500
......@@ -22,7 +22,7 @@ ARMA - arma claims
NICK . Handle half-open connections
- Figure out what causes connections to close, standardize
when we mark a connection vs when we tear it down
NICK - Look at what ssl does to keep from mutating data streams
o Look at what ssl does to keep from mutating data streams
NICK . On the fly compression of each stream
o Clean up the event loop (optimize and sanitize)
ARMA o Remove that awful concept of 'roles'
......@@ -55,8 +55,13 @@ SPEC!! D Non-clique topologies
. Directory servers
D Automated reputation management
NICK . Include key in source; sign directories
o Signed directory backend
- Document
ARMA - Integrate
- Add versions to code
NICK . Have directories list recommended-versions
o Include (unused) line in directories
o Check for presence of line.
- Quit if running the wrong version
- Command-line option to override quit
. Add more information to directory server entries
......
......@@ -551,10 +551,8 @@ int crypto_pk_public_checksig(crypto_pk_env_t *env, unsigned char *from, int fro
switch(env->type) {
case CRYPTO_PK_RSA:
if (!(((RSA*)env->key)->p))
return -1;
return RSA_public_decrypt(fromlen, from, to, (RSA *)env->key,
RSA_PKCS1_OAEP_PADDING);
RSA_PKCS1_PADDING);
default:
return -1;
}
......@@ -569,7 +567,7 @@ int crypto_pk_private_sign(crypto_pk_env_t *env, unsigned char *from, int fromle
if (!(((RSA*)env->key)->p))
return -1;
return RSA_private_encrypt(fromlen, from, to, (RSA *)env->key,
RSA_PKCS1_OAEP_PADDING);
RSA_PKCS1_PADDING);
default:
return -1;
}
......@@ -836,7 +834,7 @@ base64_encode(char *dest, int destlen, char *src, int srclen)
EVP_EncodeInit(&ctx);
EVP_EncodeUpdate(&ctx, dest, &len, src, srclen);
EVP_EncodeFinal(&ctx, dest, &ret);
EVP_EncodeFinal(&ctx, dest+len, &ret);
ret += len;
return ret;
}
......
......@@ -65,7 +65,7 @@ int crypto_pk_keysize(crypto_pk_env_t *env);
int crypto_pk_public_encrypt(crypto_pk_env_t *env, unsigned char *from, int fromlen, unsigned char *to, int padding);
int crypto_pk_private_decrypt(crypto_pk_env_t *env, unsigned char *from, int fromlen, unsigned char *to, int padding);
int crypto_pk_private_sign(crypto_pk_env_t *env, unsigned char *from, int fromlen, unsigned char *to);
int crypto_pk_private_checksig(crypto_pk_env_t *env, unsigned char *from, int fromlen, unsigned char *to);
int crypto_pk_public_checksig(crypto_pk_env_t *env, unsigned char *from, int fromlen, unsigned char *to);
int base64_encode(char *dest, int destlen, char *src, int srclen);
int base64_decode(char *dest, int destlen, char *src, int srclen);
......
......@@ -678,16 +678,17 @@ dump_signed_directory_to_string_impl(char *s, int maxlen, directory_t *dir,
dump_directory_to_string_impl(s+i, maxlen-i, dir);
i = strlen(s);
strncat(s, "directory-signature\n", maxlen-i);
i = strlen(s);
cp = s + i;
if (crypto_SHA_digest(s, i, digest))
return -1;
if (crypto_pk_private_sign(private_key, digest, 20, signature))
if (crypto_pk_private_sign(private_key, digest, 20, signature) < 0)
return -1;
strncpy(cp,
"directory-signature\n-----BEGIN SIGNATURE-----\n", maxlen-i);
"-----BEGIN SIGNATURE-----\n", maxlen-i);
i = strlen(s);
cp = s+i;
......
......@@ -739,6 +739,8 @@ int do_main_loop(void);
void dumpstats(void);
void dump_directory_to_string(char *s, int maxlen);
void dump_directory_to_string_impl(char *s, int maxlen, directory_t *directory);
int dump_signed_directory_to_string_impl(char *s, int maxlen, directory_t *dir, crypto_pk_env_t *private_key);
int main(int argc, char *argv[]);
......@@ -790,10 +792,13 @@ void router_get_directory(directory_t **pdirectory);
int router_is_me(uint32_t addr, uint16_t port);
void router_forget_router(uint32_t addr, uint16_t port);
int router_get_list_from_file(char *routerfile);
int router_resolve(routerinfo_t *router);
int router_get_list_from_string(char *s);
int router_get_list_from_string_impl(char *s, directory_t **dest);
int router_get_dir_from_string(char *s, crypto_pk_env_t *pkey);
int router_get_dir_from_string_impl(char *s, directory_t **dest,
crypto_pk_env_t *pkey);
routerinfo_t *router_get_entry_from_string(char **s);
int router_compare_to_exit_policy(connection_t *conn);
void routerlist_free(routerinfo_t *list);
......
......@@ -2,7 +2,10 @@
/* See LICENSE for licensing information */
/* $Id$ */
#define OR_PUBLICKEY_BEGIN_TAG "-----BEGIN RSA PUBLIC KEY-----\n"
#define OR_PUBLICKEY_END_TAG "-----END RSA PUBLIC KEY-----\n"
#define OR_SIGNATURE_BEGIN_TAG "-----BEGIN SIGNATURE-----\n"
#define OR_SIGNATURE_END_TAG "-----END SIGNATURE-----\n"
#include "or.h"
......@@ -16,14 +19,22 @@ extern routerinfo_t *my_routerinfo; /* from main.c */
/****************************************************************************/
struct directory_token;
typedef struct directory_token directory_token_t;
/* static function prototypes */
void routerlist_free(routerinfo_t *list);
static routerinfo_t **make_rarray(routerinfo_t* list, int *len);
static char *eat_whitespace(char *s);
static char *eat_whitespace_no_nl(char *s);
static char *find_whitespace(char *s);
static int router_resolve(routerinfo_t *router);
static void router_add_exit_policy(routerinfo_t *router, char *string);
static void router_free_exit_policy(routerinfo_t *router);
static routerinfo_t *router_get_entry_from_string_tok(char**s,
directory_token_t *tok);
static int router_get_list_from_string_tok(char **s, directory_t **dest,
directory_token_t *tok);
static int router_add_exit_policy(routerinfo_t *router,
directory_token_t *tok);
/****************************************************************************/
......@@ -288,12 +299,323 @@ int router_get_list_from_file(char *routerfile)
return 0;
}
typedef enum {
K_ACCEPT,
K_CLIENT_SOFTWARE,
K_DIRECTORY_SIGNATURE,
K_REJECT,
K_ROUTER,
K_SERVER_SOFTWARE,
K_SIGNED_DIRECTORY,
K_SIGNING_KEY,
_SIGNATURE,
_PUBLIC_KEY,
_ERR,
_EOF
} directory_keyword;
struct token_table_ent { char *t; int v; };
static struct token_table_ent token_table[] = {
{ "accept", K_ACCEPT },
{ "client-software", K_CLIENT_SOFTWARE },
{ "directory-signature", K_DIRECTORY_SIGNATURE },
{ "reject", K_REJECT },
{ "router", K_ROUTER },
{ "server-software", K_SERVER_SOFTWARE },
{ "signed-directory", K_SIGNED_DIRECTORY },
{ "signing-key", K_SIGNING_KEY },
{ NULL, -1 }
};
#define MAX_ARGS 8
struct directory_token {
directory_keyword tp;
union {
struct {
char *args[MAX_ARGS+1];
int n_args;
} cmd;
char *signature;
char *error;
crypto_pk_env_t *public_key;
} val;
};
static int
_router_get_next_token(char **s, directory_token_t *tok) {
char *next;
crypto_pk_env_t *pkey = NULL;
char *signature = NULL;
int i, done;
tok->tp = _ERR;
tok->val.error = "";
*s = eat_whitespace(*s);
if (!**s) {
tok->tp = _EOF;
return 0;
} else if (**s == '-') {
next = strchr(*s, '\n');
if (! next) { tok->val.error = "No newline at EOF"; return -1; }
++next;
if (! strncmp(*s, OR_PUBLICKEY_BEGIN_TAG, next-*s)) {
next = strstr(*s, OR_PUBLICKEY_END_TAG);
if (!next) { tok->val.error = "No public key end tag found"; return -1; }
next = strchr(next, '\n'); /* Part of OR_PUBLICKEY_END_TAG; can't fail.*/
++next;
if (!(pkey = crypto_new_pk_env(CRYPTO_PK_RSA)))
return -1;
if (crypto_pk_read_public_key_from_string(pkey, *s, next-*s)) {
crypto_free_pk_env(pkey);
tok->val.error = "Couldn't parse public key.";
return -1;
}
tok->tp = _PUBLIC_KEY;
tok->val.public_key = pkey;
*s = next;
return 0;
} else if (! strncmp(*s, OR_SIGNATURE_BEGIN_TAG, next-*s)) {
/* Advance past newline; can't fail. */
*s = strchr(*s, '\n');
++*s;
/* Find end of base64'd data */
next = strstr(*s, OR_SIGNATURE_END_TAG);
if (!next) { tok->val.error = "No signature end tag found"; return -1; }
signature = malloc(256);
i = base64_decode(signature, 256, *s, next-*s);
if (i<0) {
free(signature);
tok->val.error = "Error decoding signature."; return -1;
} else if (i != 128) {
free(signature);
tok->val.error = "Bad length on decoded signature."; return -1;
}
tok->tp = _SIGNATURE;
tok->val.signature = signature;
next = strchr(next, '\n'); /* Part of OR_SIGNATURE_END_TAG; can't fail.*/
*s = next+1;
return 0;
} else {
tok->val.error = "Unrecognized begin line"; return -1;
}
} else {
next = find_whitespace(*s);
if (!next) {
tok->val.error = "Unexpected EOF"; return -1;
}
for (i = 0 ; token_table[i].t ; ++i) {
if (!strncmp(token_table[i].t, *s, next-*s)) {
tok->tp = token_table[i].v;
i = 0;
done = (*next == '\n');
*s = eat_whitespace_no_nl(next);
while (**s != '\n' && i <= MAX_ARGS && !done) {
next = find_whitespace(*s);
if (*next == '\n')
done = 1;
*next = 0;
tok->val.cmd.args[i++] = *s;
*s = eat_whitespace_no_nl(next+1);
};
tok->val.cmd.n_args = i;
if (i > MAX_ARGS) {
tok->tp = _ERR;
tok->val.error = "Too many arguments"; return -1;
}
return 0;
}
}
tok->val.error = "Unrecognized command"; return -1;
}
}
static void
router_dump_token(directory_token_t *tok) {
int i;
switch(tok->tp)
{
case _SIGNATURE:
puts("(signature)");
return;
case _PUBLIC_KEY:
puts("(public key)");
return;
case _ERR:
printf("(Error: %s\n)", tok->val.error);
return;
case _EOF:
puts("EOF");
return;
case K_ACCEPT: printf("Accept"); break;
case K_CLIENT_SOFTWARE: printf("Client-Software"); break;
case K_DIRECTORY_SIGNATURE: printf("Directory-Signature"); break;
case K_REJECT: printf("Reject"); break;
case K_ROUTER: printf("Router"); break;
case K_SERVER_SOFTWARE: printf("Server-Software"); break;
case K_SIGNED_DIRECTORY: printf("Signed-Directory"); break;
case K_SIGNING_KEY: printf("Signing-Key"); break;
default:
printf("?????? %d\n", tok->tp); return;
}
for (i = 0; i < tok->val.cmd.n_args; ++i) {
printf(" \"%s\"", tok->val.cmd.args[i]);
}
printf("\n");
return;
}
#ifdef DEBUG_ROUTER_TOKENS
static int
router_get_next_token(char **s, directory_token_t *tok) {
int i;
i = _router_get_next_token(s, tok);
router_dump_token(tok);
return i;
}
#else
#define router_get_next_token _router_get_next_token
#endif
/* return the first char of s that is not whitespace and not a comment */
static char *eat_whitespace(char *s) {
assert(s);
while(isspace(*s) || *s == '#') {
while(isspace(*s))
s++;
if(*s == '#') { /* read to a \n or \0 */
while(*s && *s != '\n')
s++;
if(!*s)
return s;
}
}
return s;
}
static char *eat_whitespace_no_nl(char *s) {
while(*s == ' ' || *s == '\t')
++s;
return s;
}
/* return the first char of s that is whitespace or '#' or '\0 */
static char *find_whitespace(char *s) {
assert(s);
while(*s && !isspace(*s) && *s != '#')
s++;
return s;
}
int router_get_list_from_string(char *s)
{
return router_get_list_from_string_impl(s, &directory);
}
int router_get_list_from_string_impl(char *s, directory_t **dest)
int router_get_list_from_string_impl(char *s, directory_t **dest) {
directory_token_t tok;
if (router_get_next_token(&s, &tok)) {
return NULL;
}
return router_get_list_from_string_tok(&s, dest, &tok);
}
static int router_get_dir_hash(char *s, char *digest)
{
char *start, *end;
start = strstr(s, "signed-directory");
if (!start) return -1;
end = strstr(start, "directory-signature");
if (!end) return -1;
end = strchr(end, '\n');
if (!end) return -1;
++end;
if (crypto_SHA_digest(start, end-start, digest))
return -1;
return 0;
}
int router_get_dir_from_string(char *s, crypto_pk_env_t *pkey)
{
return router_get_dir_from_string_impl(s, &directory, pkey);
}
int router_get_dir_from_string_impl(char *s, directory_t **dest,
crypto_pk_env_t *pkey)
{
directory_token_t tok;
char digest[20];
char signed_digest[128];
#define NEXT_TOK() \
do { \
if (router_get_next_token(&s, &tok)) { \
log(LOG_ERR, "Error reading directory: %s", tok.val.error); \
return -1; \
} } while (0)
#define TOK_IS(type,name) \
do { \
if (tok.tp != type) { \
log(LOG_ERR, "Error reading directory: expected %s", name); \
return -1; \
} } while(0)
if (router_get_dir_hash(s, digest))
return -1;
NEXT_TOK();
TOK_IS(K_SIGNED_DIRECTORY, "signed-directory");
NEXT_TOK();
TOK_IS(K_CLIENT_SOFTWARE, "client-software");
NEXT_TOK();
TOK_IS(K_SERVER_SOFTWARE, "server-software");
NEXT_TOK();
if (router_get_list_from_string_tok(&s, dest, &tok))
return -1;
TOK_IS(K_DIRECTORY_SIGNATURE, "directory-signature");
NEXT_TOK();
TOK_IS(_SIGNATURE, "signature");
if (pkey) {
if (crypto_pk_public_checksig(pkey, tok.val.signature, 128, signed_digest)
!= 20) {
log(LOG_ERR, "Error reading directory: invalid signature.");
free(tok.val.signature);
return -1;
}
if (memcmp(digest, signed_digest, 20)) {
log(LOG_ERR, "Error reading directory: signature does not match.");
free(tok.val.signature);
return -1;
}
}
free(tok.val.signature);
NEXT_TOK();
TOK_IS(_EOF, "end of directory");
return 0;
#undef NEXT_TOK
#undef TOK_IS
}
static int router_get_list_from_string_tok(char **s, directory_t **dest,
directory_token_t *tok)
{
routerinfo_t *routerlist=NULL;
routerinfo_t *router;
......@@ -302,17 +624,8 @@ int router_get_list_from_string_impl(char *s, directory_t **dest)
assert(s);
while(*s) { /* while not at the end of the string */
router = router_get_entry_from_string(&s);
if(router == NULL) {
routerlist_free(routerlist);
return -1;
}
if (router_resolve(router)) {
routerlist_free(router);
routerlist_free(routerlist);
return -1;
}
while (tok->tp == K_ROUTER) {
router = router_get_entry_from_string_tok(s, tok);
switch(router_is_me(router->addr, router->or_port)) {
case 0: /* it's not me */
router->next = routerlist;
......@@ -329,7 +642,6 @@ int router_get_list_from_string_impl(char *s, directory_t **dest)
routerlist_free(routerlist);
return -1;
}
s = eat_whitespace(s);
}
new_router_array = make_rarray(routerlist, &new_rarray_len);
......@@ -343,34 +655,6 @@ int router_get_list_from_string_impl(char *s, directory_t **dest)
}
return -1;
}
/* return the first char of s that is not whitespace and not a comment */
static char *eat_whitespace(char *s) {
assert(s);
while(isspace(*s) || *s == '#') {
while(isspace(*s))
s++;
if(*s == '#') { /* read to a \n or \0 */
while(*s && *s != '\n')
s++;
if(!*s)
return s;
}
}
return s;
}
/* return the first char of s that is whitespace or '#' or '\0 */
static char *find_whitespace(char *s) {
assert(s);
while(*s && !isspace(*s) && *s != '#')
s++;
return s;
}
static int
router_resolve(routerinfo_t *router)
{
......@@ -388,157 +672,102 @@ router_resolve(routerinfo_t *router)
return 0;
}
routerinfo_t *router_get_entry_from_string(char **s) {
directory_token_t tok;
routerinfo_t *router;
if (router_get_next_token(s, &tok)) return NULL;
router = router_get_entry_from_string_tok(s, &tok);
if (tok.tp != _EOF)
return NULL;
return router;
}
/* reads a single router entry from s.
* updates s so it points to after the router it just read.
* mallocs a new router, returns it if all goes well, else returns NULL.
*/
routerinfo_t *router_get_entry_from_string(char **s) {
routerinfo_t *router;
char *next;
static routerinfo_t *router_get_entry_from_string_tok(char**s, directory_token_t *tok) {
routerinfo_t *router = NULL;
/* Make sure that this string really starts with a router entry. */
*s = eat_whitespace(*s);
if (strncasecmp(*s, "router ", 7)) {
#define NEXT_TOKEN() \
do { if (router_get_next_token(s, tok)) goto err; \
} while(0)
#define ARGS tok->val.cmd.args
if (tok->tp != K_ROUTER) {
log(LOG_ERR,"router_get_entry_from_string(): Entry does not start with \"router\"");
return NULL;
}
router = malloc(sizeof(routerinfo_t));
if (!router) {
if (!(router = malloc(sizeof(routerinfo_t)))) {
log(LOG_ERR,"router_get_entry_from_string(): Could not allocate memory.");
return NULL;
}
memset(router,0,sizeof(routerinfo_t)); /* zero it out first */
router->next = NULL;
/* Bug: if find_whitespace returns a '#', we'll squish it. */
#define NEXT_TOKEN(s, next) \
*s = eat_whitespace(*s); \
next = find_whitespace(*s); \
if(!*next) { \
goto router_read_failed; \
} \
*next = 0;
/* Skip the "router" */
NEXT_TOKEN(s, next);
*s = next+1;
/* read router->address */
NEXT_TOKEN(s, next);
router->address = strdup(*s);
*s = next+1;
/* Don't resolve address till later. */
if (tok->val.cmd.n_args != 6) {
log(LOG_ERR,"router_get_entry_from_string(): Wrong # of arguments to \"router\"");
goto err;
}
/* read router.address */
if (!(router->address = strdup(ARGS[0])))
goto err;
router->addr = 0;
/* read router->or_port */
NEXT_TOKEN(s, next);
router->or_port = atoi(*s);
/* Read router->or_port */
router->or_port = atoi(ARGS[1]);
if(!router->or_port) {
log(LOG_ERR,"router_get_entry_from_string(): or_port '%s' unreadable or 0. Failing.",*s);
goto router_read_failed;
log(LOG_ERR,"router_get_entry_from_string(): or_port unreadable or 0. Failing.");
goto err;
}
*s = next+1;
/* read router->op_port */
NEXT_TOKEN(s, next);
<