commit ebd15e8027e89ca3027c2ffc2c629a8a552cb772 Author: Zack Weinberg zackw@panix.com Date: Sun Jul 24 13:49:51 2011 -0700
Allow protocols to allocate extra state for their protocol_params_t as well as their protocol_t. Use this to remove the obfs2 shared secret from the generic protocol_params_t. --- src/protocol.c | 15 ++++-- src/protocol.h | 62 +++++++++++------------ src/protocols/dummy.c | 7 +++ src/protocols/obfs2.c | 131 ++++++++++++++++++++++++++---------------------- src/protocols/obfs2.h | 6 ++- 5 files changed, 122 insertions(+), 99 deletions(-)
diff --git a/src/protocol.c b/src/protocol.c index 8cd069e..212ddaa 100644 --- a/src/protocol.c +++ b/src/protocol.c @@ -52,14 +52,19 @@ void proto_params_free(protocol_params_t *params) { obfs_assert(params); + obfs_assert(params->vtable); + obfs_assert(params->vtable->fini);
- if (params->target_addr) + if (params->target_addr) { evutil_freeaddrinfo(params->target_addr); - if (params->listen_addr) + params->target_addr = NULL; + } + if (params->listen_addr) { evutil_freeaddrinfo(params->listen_addr); - if (params->shared_secret) - free(params->shared_secret); - free(params); + params->listen_addr = NULL; + } + + params->vtable->fini(params); }
/** diff --git a/src/protocol.h b/src/protocol.h index 6fa3eb4..c1189ea 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -11,39 +11,28 @@ struct evbuffer;
/** - This struct defines parameters of a protocol on a per-listener basis. - - By 'per-listener basis' I mean that the parameters defined here will - be inherited by *all* connections opened from the listener_t that - owns this protocol_params_t. -*/ + This struct defines the protocol-specific state for all connections + opened from a particular listener. Each protocol may extend this + structure with additional private data by embedding it as the first + member of a larger structure (standard fake-inheritance-in-C + technique). + */ typedef struct protocol_params_t { const struct protocol_vtable *vtable; struct evutil_addrinfo *target_addr; struct evutil_addrinfo *listen_addr; - char *shared_secret; - size_t shared_secret_len; int mode; } protocol_params_t;
/** - This protocol specific struct defines the state of the protocol - on a per-connection basis. - - By 'protocol specific' I mean that every protocol has its own - state struct. (for example, obfs2 has obfs2_state_t). A protocol_t - struct is always the first member of this struct, and vtable->create - returns that member (standard fake-inheritance-in-C technique). - All data other than the vtable is hidden from everything but the - protocol implementation. - - By 'per-connection basis' I mean that the every connection has a - different protocol_t struct, and that's precisely the reason that - this struct is owned by the conn_t struct. + This struct defines the protocol-specific state for a particular + connection. Again, each protocol may extend this structure with + additional private data by embedding it as the first member of a + larger structure. */ -struct protocol_t { +typedef struct protocol_t { const struct protocol_vtable *vtable; -}; +} protocol_t;
/** This struct defines a protocol and its methods; note that not all @@ -58,23 +47,29 @@ typedef struct protocol_vtable /** The short name of this protocol. */ const char *name;
- /** Initialization function: Allocate a 'protocol_params_t' object - and fill it in from the provided 'options' array. */ - struct protocol_params_t *(*init)(int n_options, - const char *const *options); + /** Initialization: Allocate a 'protocol_params_t' object and fill + it in from the provided 'options' array. */ + protocol_params_t *(*init)(int n_options, const char *const *options); + + /** Finalization: Destroy the provided 'protocol_params_t' object. + This function is responsible for deallocating any data that the + protocol's extended structure points to, and deallocating the + object itself. But it is *not* responsible for deallocating the + data pointed to by the generic 'protocol_params_t'; that's + already been done. */ + void (*fini)(protocol_params_t *params);
/** Constructor: Allocates per-connection, protocol-specific state. */ - struct protocol_t *(*create)(struct protocol_params_t *params); + protocol_t *(*create)(protocol_params_t *params);
/** Destructor: Destroys per-connection, protocol-specific state. */ - void (*destroy)(struct protocol_t *state); + void (*destroy)(protocol_t *state);
/** Perform a connection handshake. Not all protocols have a handshake. */ - int (*handshake)(struct protocol_t *state, - struct evbuffer *buf); + int (*handshake)(protocol_t *state, struct evbuffer *buf);
/** Send data coming downstream from 'source' along to 'dest'. */ - int (*send)(struct protocol_t *state, + int (*send)(protocol_t *state, struct evbuffer *source, struct evbuffer *dest);
@@ -93,7 +88,8 @@ typedef struct protocol_vtable #define DEFINE_PROTOCOL_VTABLE(name) \ const struct protocol_vtable name##_vtable = { \ #name, \ - name##_init, name##_create, name##_destroy, \ + name##_init, name##_fini, \ + name##_create, name##_destroy, \ name##_handshake, name##_send, name##_recv \ }
diff --git a/src/protocols/dummy.c b/src/protocols/dummy.c index c12fe01..ae45d9c 100644 --- a/src/protocols/dummy.c +++ b/src/protocols/dummy.c @@ -29,6 +29,7 @@ dummy_init(int n_options, const char *const *options) { struct protocol_params_t *params = xzalloc(sizeof(struct protocol_params_t)); + params->vtable = &dummy_vtable;
if (parse_and_set_options(n_options, options, params) < 0) { proto_params_free(params); @@ -87,6 +88,12 @@ usage(void) "\tobfsproxy dummy socks 127.0.0.1:5000"); }
+static void +dummy_fini(struct protocol_params_t *params) +{ + free(params); +} + /* This is called everytime we get a connection for the dummy protocol. diff --git a/src/protocols/obfs2.c b/src/protocols/obfs2.c index 42d30c1..f7862fe 100644 --- a/src/protocols/obfs2.c +++ b/src/protocols/obfs2.c @@ -16,13 +16,22 @@ static void usage(void); static int parse_and_set_options(int n_options, const char *const *options, - struct protocol_params_t *params); + obfs2_params_t *params);
-static inline obfs2_protocol_t * -downcast(struct protocol_t *proto) +/** Return true iff the OBFUSCATE_SEED_LENGTH-byte seed in 'seed' is nonzero */ +static inline int +seed_nonzero(const uchar *seed) { - return (obfs2_protocol_t *) - ((char *)proto - offsetof(obfs2_protocol_t, super)); + static const uchar OBFUSCATE_ZERO_SEED[OBFUSCATE_SEED_LENGTH] = {0}; + return memcmp(seed, OBFUSCATE_ZERO_SEED, OBFUSCATE_SEED_LENGTH) != 0; +} + +/** Return true iff the SHARED_SECRET_LENGTH-byte seed in 'seed' is nonzero */ +static inline int +shared_seed_nonzero(const uchar *seed) +{ + static const uchar SHARED_ZERO_SEED[SHARED_SECRET_LENGTH] = {0}; + return memcmp(seed, SHARED_ZERO_SEED, SHARED_SECRET_LENGTH) != 0; }
/* @@ -31,19 +40,19 @@ downcast(struct protocol_t *proto)
Returns 0 on success, -1 on fail. */ -static struct protocol_params_t * +static protocol_params_t * obfs2_init(int n_options, const char *const *options) { - struct protocol_params_t *params - = xzalloc(sizeof(struct protocol_params_t)); + obfs2_params_t *params = xzalloc(sizeof(obfs2_params_t));
+ params->super.vtable = &obfs2_vtable; if (parse_and_set_options(n_options, options, params) < 0) { - proto_params_free(params); + proto_params_free(¶ms->super); usage(); return NULL; }
- return params; + return ¶ms->super; }
/** @@ -51,7 +60,7 @@ obfs2_init(int n_options, const char *const *options) */ int parse_and_set_options(int n_options, const char *const *options, - struct protocol_params_t *params) + obfs2_params_t *params) { int got_dest=0; int got_ss=0; @@ -67,17 +76,23 @@ parse_and_set_options(int n_options, const char *const *options, if (!strncmp(*options,"--dest=",7)) { if (got_dest) return -1; - params->target_addr = resolve_address_port(*options+7, 1, 0, NULL); - if (!params->target_addr) + params->super.target_addr = + resolve_address_port(*options+7, 1, 0, NULL); + if (!params->super.target_addr) return -1; got_dest=1; } else if (!strncmp(*options,"--shared-secret=",16)) { + digest_t *c; if (got_ss) return -1; - /* this is freed in proto_params_free() */ - params->shared_secret_len = strlen(*options+16); - params->shared_secret = xmemdup(*options+16, - params->shared_secret_len + 1); + + /* ASN we must say in spec that we hash command line shared + secret. */ + c = digest_new(); + digest_update(c, (uchar*)*options+16, strlen(*options+16)); + digest_getdigest(c, params->shared_secret, SHARED_SECRET_LENGTH); + digest_free(c); + got_ss=1; } else { log_warn("obfs2: Unknown argument."); @@ -88,37 +103,36 @@ parse_and_set_options(int n_options, const char *const *options,
if (!strcmp(*options, "client")) { defport = "48988"; /* bf5c */ - params->mode = LSN_SIMPLE_CLIENT; + params->super.mode = LSN_SIMPLE_CLIENT; } else if (!strcmp(*options, "socks")) { defport = "23548"; /* 5bf5 */ - params->mode = LSN_SOCKS_CLIENT; + params->super.mode = LSN_SOCKS_CLIENT; } else if (!strcmp(*options, "server")) { defport = "11253"; /* 2bf5 */ - params->mode = LSN_SIMPLE_SERVER; + params->super.mode = LSN_SIMPLE_SERVER; } else { log_warn("obfs2: only client/socks/server modes supported."); return -1; } options++;
- params->listen_addr = resolve_address_port(*options, 1, 1, defport); - if (!params->listen_addr) + params->super.listen_addr = resolve_address_port(*options, 1, 1, defport); + if (!params->super.listen_addr) return -1;
/* Validate option selection. */ - if (got_dest && (params->mode == LSN_SOCKS_CLIENT)) { + if (got_dest && (params->super.mode == LSN_SOCKS_CLIENT)) { log_warn("obfs2: You can't be on socks mode and have --dest."); return -1; }
- if (!got_dest && (params->mode != LSN_SOCKS_CLIENT)) { + if (!got_dest && (params->super.mode != LSN_SOCKS_CLIENT)) { log_warn("obfs2: client/server mode needs --dest."); return -1; }
log_debug("obfs2: Parsed options nicely!");
- params->vtable = &obfs2_vtable; return 0; }
@@ -142,13 +156,6 @@ usage(void) "\tobfs2 server 127.0.0.1:1026"); }
-/** Return true iff the OBFUSCATE_SEED_LENGTH-byte seed in 'seed' is nonzero */ -static int -seed_nonzero(const uchar *seed) -{ - return memcmp(seed, OBFUSCATE_ZERO_SEED, OBFUSCATE_SEED_LENGTH) != 0; -} - /** Derive and return key of type 'keytype' from the seeds currently set in 'state'. @@ -166,12 +173,12 @@ derive_key(void *s, const char *keytype) digest_update(c, state->initiator_seed, OBFUSCATE_SEED_LENGTH); if (seed_nonzero(state->responder_seed)) digest_update(c, state->responder_seed, OBFUSCATE_SEED_LENGTH); - if (seed_nonzero(state->secret_seed)) + if (shared_seed_nonzero(state->secret_seed)) digest_update(c, state->secret_seed, SHARED_SECRET_LENGTH); digest_update(c, (uchar*)keytype, strlen(keytype)); digest_getdigest(c, buf, sizeof(buf));
- if (seed_nonzero(state->secret_seed)) { + if (shared_seed_nonzero(state->secret_seed)) { digest_t *d; int i; for (i=0; i < OBFUSCATE_HASH_ITERATIONS; i++) { @@ -206,13 +213,13 @@ derive_padding_key(void *s, const uchar *seed, digest_update(c, (uchar*)keytype, strlen(keytype)); if (seed_nonzero(seed)) digest_update(c, seed, OBFUSCATE_SEED_LENGTH); - if (seed_nonzero(state->secret_seed)) + if (shared_seed_nonzero(state->secret_seed)) digest_update(c, state->secret_seed, OBFUSCATE_SEED_LENGTH); digest_update(c, (uchar*)keytype, strlen(keytype)); digest_getdigest(c, buf, sizeof(buf)); digest_free(c);
- if (seed_nonzero(state->secret_seed)) { + if (shared_seed_nonzero(state->secret_seed)) { digest_t *d; int i; for (i=0; i < OBFUSCATE_HASH_ITERATIONS; i++) { @@ -230,22 +237,32 @@ derive_padding_key(void *s, const uchar *seed, }
/** + Frees obfs2 parameters 'p' + */ +static void +obfs2_fini(protocol_params_t *p) +{ + obfs2_params_t *params = DOWNCAST(obfs2_params_t, super, p); + /* wipe out keys */ + memset(params, 0x99, sizeof(obfs2_params_t)); + free(params); +} + + +/** This is called everytime we get a connection for the obfs2 protocol. - - It sets up the protocol vtable in 'proto_struct' and then attempts - to create and return a protocol state according to the protocol - parameters 'params'. */ -static struct protocol_t * -obfs2_create(protocol_params_t *params) +static protocol_t * +obfs2_create(protocol_params_t *p) { + obfs2_params_t *params = DOWNCAST(obfs2_params_t, super, p); obfs2_protocol_t *proto = xzalloc(sizeof(obfs2_protocol_t)); uchar *seed; const char *send_pad_type;
proto->state = ST_WAIT_FOR_KEY; - proto->we_are_initiator = (params->mode != LSN_SIMPLE_SERVER); + proto->we_are_initiator = (params->super.mode != LSN_SIMPLE_SERVER); if (proto->we_are_initiator) { send_pad_type = INITIATOR_PAD_TYPE; seed = proto->initiator_seed; @@ -255,19 +272,13 @@ obfs2_create(protocol_params_t *params) }
/* Generate our seed */ + memcpy(proto->secret_seed, params->shared_secret, SHARED_SECRET_LENGTH); + if (random_bytes(seed, OBFUSCATE_SEED_LENGTH) < 0) { free(proto); return NULL; }
- if (params->shared_secret) { - /* ASN we must say in spec that we hash command line shared secret. */ - digest_t *c = digest_new(); - digest_update(c, (uchar*)params->shared_secret, params->shared_secret_len); - digest_getdigest(c, proto->secret_seed, SHARED_SECRET_LENGTH); - digest_free(c); - } - /* Derive the key for what we're sending */ proto->send_padding_crypto = derive_padding_key(proto, seed, send_pad_type); proto->super.vtable = &obfs2_vtable; @@ -278,9 +289,9 @@ obfs2_create(protocol_params_t *params) Frees obfs2 state 's' */ static void -obfs2_destroy(struct protocol_t *s) +obfs2_destroy(protocol_t *s) { - obfs2_protocol_t *state = downcast(s); + obfs2_protocol_t *state = DOWNCAST(obfs2_protocol_t, super, s); if (state->send_crypto) crypt_free(state->send_crypto); if (state->send_padding_crypto) @@ -301,9 +312,9 @@ obfs2_destroy(struct protocol_t *s) the evbuffer 'buf'. Return 0 on success, -1 on failure. */ static int -obfs2_handshake(struct protocol_t *s, struct evbuffer *buf) +obfs2_handshake(protocol_t *s, struct evbuffer *buf) { - obfs2_protocol_t *state = downcast(s); + obfs2_protocol_t *state = DOWNCAST(obfs2_protocol_t, super, s);
uint32_t magic = htonl(OBFUSCATE_MAGIC_VALUE), plength, send_plength; uchar msg[OBFUSCATE_MAX_PADDING + OBFUSCATE_SEED_LENGTH + 8]; @@ -367,10 +378,10 @@ obfs2_crypt_and_transmit(crypt_t *crypto, using the state in 'state'. Returns 0 on success, -1 on failure. */ static int -obfs2_send(struct protocol_t *s, - struct evbuffer *source, struct evbuffer *dest) +obfs2_send(protocol_t *s, + struct evbuffer *source, struct evbuffer *dest) { - obfs2_protocol_t *state = downcast(s); + obfs2_protocol_t *state = DOWNCAST(obfs2_protocol_t, super, s);
if (state->send_crypto) { /* First of all, send any data that we've been waiting to send. */ @@ -440,10 +451,10 @@ init_crypto(void *s) * our callers that they must call obfs2_send() immediately. */ static enum recv_ret -obfs2_recv(struct protocol_t *s, struct evbuffer *source, +obfs2_recv(protocol_t *s, struct evbuffer *source, struct evbuffer *dest) { - obfs2_protocol_t *state = downcast(s); + obfs2_protocol_t *state = DOWNCAST(obfs2_protocol_t, super, s); enum recv_ret r=0;
if (state->state == ST_WAIT_FOR_KEY) { diff --git a/src/protocols/obfs2.h b/src/protocols/obfs2.h index e93fbcd..eb55a9f 100644 --- a/src/protocols/obfs2.h +++ b/src/protocols/obfs2.h @@ -25,7 +25,6 @@ extern const struct protocol_vtable obfs2_vtable; #define OBFUSCATE_MAGIC_VALUE 0x2BF5CA7E #define OBFUSCATE_SEED_LENGTH 16 #define OBFUSCATE_MAX_PADDING 8192 -#define OBFUSCATE_ZERO_SEED "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" #define OBFUSCATE_HASH_ITERATIONS 100000
#define INITIATOR_PAD_TYPE "Initiator obfuscation padding" @@ -35,6 +34,11 @@ extern const struct protocol_vtable obfs2_vtable;
#define SHARED_SECRET_LENGTH SHA256_LENGTH
+typedef struct obfs2_params_t { + protocol_params_t super; + uchar shared_secret[SHARED_SECRET_LENGTH]; +} obfs2_params_t; + typedef struct obfs2_protocol_t { struct protocol_t super;
tor-commits@lists.torproject.org