connection_or.c 23.2 KB
Newer Older
1
2
3
/* Copyright 2001,2002 Roger Dingledine, Matej Pfajfar. */
/* See LICENSE for licensing information */
/* $Id$ */
Roger Dingledine's avatar
Roger Dingledine committed
4
5

#include "or.h"
6

7
extern or_options_t options; /* command-line and config-file options */
Roger Dingledine's avatar
Roger Dingledine committed
8

Roger Dingledine's avatar
Roger Dingledine committed
9
#ifndef USE_TLS
10
11
12
13
14
15
16
17
18
19
static int or_handshake_op_send_keys(connection_t *conn);
static int or_handshake_op_finished_sending_keys(connection_t *conn);

static int or_handshake_client_process_auth(connection_t *conn);
static int or_handshake_client_send_auth(connection_t *conn);

static int or_handshake_server_process_auth(connection_t *conn);
static int or_handshake_server_process_nonce(connection_t *conn);

static void conn_or_init_crypto(connection_t *conn);
20
static void connection_or_set_open(connection_t *conn);
21
#endif
22

Roger Dingledine's avatar
Roger Dingledine committed
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/**************************************************************/

static void cell_pack(char *dest, const cell_t *src) {
  *(uint16_t*)dest     = htons(src->aci);
  *(uint8_t*)(dest+2)  = src->command;
  *(uint8_t*)(dest+3)  = src->length;
  *(uint32_t*)(dest+4) = 0; /* Reserved */
  memcpy(dest+8, src->payload, CELL_PAYLOAD_SIZE);
}

static void cell_unpack(cell_t *dest, const char *src) {
  dest->aci     = ntohs(*(uint16_t*)(src));
  dest->command = *(uint8_t*)(src+2);
  dest->length  = *(uint8_t*)(src+3);
  dest->seq     = ntohl(*(uint32_t*)(src+4));
  memcpy(dest->payload, src+8, CELL_PAYLOAD_SIZE);
}

/**************************************************************/
Roger Dingledine's avatar
Roger Dingledine committed
42
43
44
45
46
47

int connection_or_process_inbuf(connection_t *conn) {

  assert(conn && conn->type == CONN_TYPE_OR);

  if(conn->inbuf_reached_eof) {
48
    log_fn(LOG_DEBUG,"conn reached eof. Closing.");
Roger Dingledine's avatar
Roger Dingledine committed
49
50
51
    return -1;
  }

Roger Dingledine's avatar
Roger Dingledine committed
52
#ifdef USE_TLS
53
54
  if(conn->state != OR_CONN_STATE_OPEN)
    return 0; /* don't do anything */
Roger Dingledine's avatar
Roger Dingledine committed
55
56
  return connection_process_cell_from_inbuf(conn);
#else
57
//  log(LOG_DEBUG,"connection_or_process_inbuf(): state %d.",conn->state);
Roger Dingledine's avatar
Roger Dingledine committed
58
59
60
61
62
63
64
65
66
67
  switch(conn->state) {
    case OR_CONN_STATE_CLIENT_AUTH_WAIT:
      return or_handshake_client_process_auth(conn);
    case OR_CONN_STATE_SERVER_AUTH_WAIT:
      return or_handshake_server_process_auth(conn);
    case OR_CONN_STATE_SERVER_NONCE_WAIT:
      return or_handshake_server_process_nonce(conn);
    case OR_CONN_STATE_OPEN:
      return connection_process_cell_from_inbuf(conn);
    default:
68
      log_fn(LOG_DEBUG,"called in state where I'm writing. Ignoring buf for now.");
Roger Dingledine's avatar
Roger Dingledine committed
69
70
71
  }

  return 0;
Roger Dingledine's avatar
Roger Dingledine committed
72
#endif
Roger Dingledine's avatar
Roger Dingledine committed
73
74
75
76
77
78
79
80
}

int connection_or_finished_flushing(connection_t *conn) {
  int e, len=sizeof(e);

  assert(conn && conn->type == CONN_TYPE_OR);

  switch(conn->state) {
Roger Dingledine's avatar
Roger Dingledine committed
81
#ifndef USE_TLS
82
83
    case OR_CONN_STATE_OP_SENDING_KEYS:
      return or_handshake_op_finished_sending_keys(conn);
Roger Dingledine's avatar
Roger Dingledine committed
84
    case OR_CONN_STATE_CLIENT_CONNECTING:
Roger Dingledine's avatar
Roger Dingledine committed
85
86
87
#else
    case OR_CONN_STATE_CONNECTING:
#endif
88
      if (getsockopt(conn->s, SOL_SOCKET, SO_ERROR, (void*)&e, &len) < 0)  { /* not yet */
89
        if(!ERRNO_CONN_EINPROGRESS(errno)){
90
          log_fn(LOG_DEBUG,"in-progress connect failed. Removing.");
Roger Dingledine's avatar
Roger Dingledine committed
91
92
93
94
95
96
97
          return -1;
        } else {
          return 0; /* no change, see if next time is better */
        }
      }
      /* the connect has finished. */

98
      log_fn(LOG_DEBUG,"OR connect() to router %s:%u finished.",
99
          conn->address,conn->port);
Roger Dingledine's avatar
Roger Dingledine committed
100

Roger Dingledine's avatar
Roger Dingledine committed
101
#ifdef USE_TLS
102
      if(connection_tls_start_handshake(conn, 0) < 0)
103
        return -1;
Roger Dingledine's avatar
Roger Dingledine committed
104
#else
105
106
107
108
      if(options.OnionRouter)
        return or_handshake_client_send_auth(conn);
      else
        return or_handshake_op_send_keys(conn);
Roger Dingledine's avatar
Roger Dingledine committed
109
    case OR_CONN_STATE_CLIENT_SENDING_AUTH:
110
      log_fn(LOG_DEBUG,"client finished sending auth.");
Roger Dingledine's avatar
Roger Dingledine committed
111
112
113
114
      conn->state = OR_CONN_STATE_CLIENT_AUTH_WAIT;
      connection_watch_events(conn, POLLIN);
      return 0;
    case OR_CONN_STATE_CLIENT_SENDING_NONCE:
115
      log_fn(LOG_DEBUG,"client finished sending nonce.");
Roger Dingledine's avatar
Roger Dingledine committed
116
      conn_or_init_crypto(conn);
Roger Dingledine's avatar
Roger Dingledine committed
117
118
      connection_or_set_open(conn);

119
      return connection_process_inbuf(conn); /* in case there's anything waiting on it */
Roger Dingledine's avatar
Roger Dingledine committed
120
    case OR_CONN_STATE_SERVER_SENDING_AUTH:
121
      log_fn(LOG_DEBUG,"server finished sending auth.");
Roger Dingledine's avatar
Roger Dingledine committed
122
123
124
      conn->state = OR_CONN_STATE_SERVER_NONCE_WAIT;
      connection_watch_events(conn, POLLIN);
      return 0;
Roger Dingledine's avatar
Roger Dingledine committed
125
#endif
Roger Dingledine's avatar
Roger Dingledine committed
126
    case OR_CONN_STATE_OPEN:
127
      connection_stop_writing(conn);
Roger Dingledine's avatar
Roger Dingledine committed
128
129
      return 0;
    default:
Roger Dingledine's avatar
Roger Dingledine committed
130
      log_fn(LOG_ERR,"BUG: called in unexpected state.");
Roger Dingledine's avatar
Roger Dingledine committed
131
132
133
134
135
136
      return 0;
  }
}

/*********************/

137
connection_t *connection_or_connect(routerinfo_t *router) {
Roger Dingledine's avatar
Roger Dingledine committed
138
139
  connection_t *conn;

140
141
142
143
144
145
146
147
148
149
150
151
152
153
  assert(router);

  if(router_is_me(router->addr, router->or_port)) {
    /* this is me! don't connect to me. */
    log(LOG_DEBUG,"connection_or_connect(): This is me. Skipping.");
    return NULL;
  }

  /* this function should never be called if we're already connected to router, but */
  /* check first to be sure */
  conn = connection_exact_get_by_addr_port(router->addr,router->or_port);
  if(conn)
    return conn;

154
  conn = connection_new(CONN_TYPE_OR);
Roger Dingledine's avatar
Roger Dingledine committed
155
  if(!conn) {
156
    return NULL;
Roger Dingledine's avatar
Roger Dingledine committed
157
  }
Roger Dingledine's avatar
Roger Dingledine committed
158
159

  /* set up conn so it's got all the data we need to remember */
160
  conn->addr = router->addr;
161
  conn->port = router->or_port;
162
163
  conn->bandwidth = router->bandwidth;
  conn->pkey = crypto_pk_dup_key(router->pkey);
Roger Dingledine's avatar
Roger Dingledine committed
164
165
  conn->address = strdup(router->address);

166
  if(connection_add(conn) < 0) { /* no space, forget it */
Roger Dingledine's avatar
Roger Dingledine committed
167
    connection_free(conn);
168
    return NULL;
Roger Dingledine's avatar
Roger Dingledine committed
169
170
  }

171
172
173
  switch(connection_connect(conn, router->address, router->addr, router->or_port)) {
    case -1:
      connection_remove(conn);
Roger Dingledine's avatar
Roger Dingledine committed
174
      connection_free(conn);
175
      return NULL;
176
177
    case 0:
      connection_set_poll_socket(conn);
178
179
180
      connection_watch_events(conn, POLLIN | POLLOUT | POLLERR); 
      /* writable indicates finish, readable indicates broken link,
         error indicates broken link on windows */
181
182
183
#ifdef USE_TLS
      conn->state = OR_CONN_STATE_CONNECTING;
#else
184
      conn->state = OR_CONN_STATE_CLIENT_CONNECTING;
185
#endif
186
      return conn;
187
    /* case 1: fall through */
188
189
  }

190
  connection_set_poll_socket(conn);
191

192
#ifdef USE_TLS
193
  if(connection_tls_start_handshake(conn, 0) >= 0)
194
195
    return conn;
#else
196
197
198
  if((options.OnionRouter && or_handshake_client_send_auth(conn) >= 0) ||
     (!options.OnionRouter && or_handshake_op_send_keys(conn) >= 0))
    return conn; /* success! */
199
#endif
200

201
202
203
204
  /* failure */
  connection_remove(conn);
  connection_free(conn);
  return NULL;
205
206
}

207
208
/* ********************************** */

209
#ifndef USE_TLS
210
211
212
213
/* Helper functions to implement handshaking */

#define FLAGS_LEN 2
#define KEY_LEN 16
214
215
#define ADDR_LEN 4
#define PORT_LEN 2
216
217
218
219
#define PKEY_LEN 128

static int 
or_handshake_op_send_keys(connection_t *conn) {
220
  unsigned char message[FLAGS_LEN + KEY_LEN + KEY_LEN];
221
  unsigned char cipher[PKEY_LEN];
222
223
224
225
  int retval;

  assert(conn && conn->type == CONN_TYPE_OR);

226
  conn->bandwidth = DEFAULT_BANDWIDTH_OP;
227

228
  /* generate random keys */
229
230
  if(crypto_cipher_generate_key(conn->f_crypto) ||
     crypto_cipher_generate_key(conn->b_crypto)) {
Nick Mathewson's avatar
src/or    
Nick Mathewson committed
231
    log(LOG_ERR,"Cannot generate a secure symmetric key.");
232
233
    return -1;
  }
Nick Mathewson's avatar
src/or    
Nick Mathewson committed
234
  log(LOG_DEBUG,"or_handshake_op_send_keys() : Generated symmetric keys.");
235
  /* compose the message */
236
  *(uint16_t *)(message) = htons(HANDSHAKE_AS_OP);
237
  memcpy((void *)(message+FLAGS_LEN), 
238
         (void *)crypto_cipher_get_key(conn->f_crypto), 16);
239
  memcpy((void *)(message+FLAGS_LEN+KEY_LEN), 
240
         (void *)crypto_cipher_get_key(conn->b_crypto), 16);
241
242

  /* encrypt with RSA */
243
  if(crypto_pk_public_encrypt(conn->pkey, message, sizeof(message), cipher, RSA_PKCS1_PADDING) < 0) {
244
    log(LOG_ERR,"or_handshake_op_send_keys(): Public key encryption failed.");
Roger Dingledine's avatar
Roger Dingledine committed
245
246
    return -1;
  }
247
  log(LOG_DEBUG,"or_handshake_op_send_keys() : Encrypted authentication message.");
Roger Dingledine's avatar
Roger Dingledine committed
248

249
  /* send message */
Roger Dingledine's avatar
Roger Dingledine committed
250

251
  if(connection_write_to_buf(cipher, PKEY_LEN, conn) < 0) {
252
    log(LOG_DEBUG,"or_handshake_op_send_keys(): my outbuf is full. Oops.");
Roger Dingledine's avatar
Roger Dingledine committed
253
254
    return -1;
  }
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
  retval = connection_flush_buf(conn);
  if(retval < 0) {
    log(LOG_DEBUG,"or_handshake_op_send_keys(): bad socket while flushing.");
    return -1;
  }
  if(retval > 0) {
    /* still stuff on the buffer. */
    conn->state = OR_CONN_STATE_OP_SENDING_KEYS;
    connection_watch_events(conn, POLLOUT | POLLIN);
    return 0;
  }

  /* it finished sending */
  log(LOG_DEBUG,"or_handshake_op_send_keys(): Finished sending authentication message.");
  return or_handshake_op_finished_sending_keys(conn);
}

272
273
static int 
or_handshake_op_finished_sending_keys(connection_t *conn) {
274
275
276
277

  /* do crypto initialization, etc */
  conn_or_init_crypto(conn);

Roger Dingledine's avatar
Roger Dingledine committed
278
  connection_or_set_open(conn);
279
  circuit_n_conn_open(conn); /* send the pending onion(s) */
280
  return 0;
281
282
}

283
284
static int 
or_handshake_client_send_auth(connection_t *conn) {
Roger Dingledine's avatar
Roger Dingledine committed
285
  int retval;
286
  char buf[FLAGS_LEN+ADDR_LEN+PORT_LEN+ADDR_LEN+
287
           PORT_LEN+KEY_LEN+KEY_LEN];
288
  char cipher[PKEY_LEN];
289
  struct sockaddr_in me; /* my router identity */
Roger Dingledine's avatar
Roger Dingledine committed
290

291
  assert(conn);
Roger Dingledine's avatar
Roger Dingledine committed
292

293
294
295
  if(learn_my_address(&me) < 0)
    return -1;

Roger Dingledine's avatar
Roger Dingledine committed
296
  /* generate random keys */
297
298
  if(crypto_cipher_generate_key(conn->f_crypto) ||
     crypto_cipher_generate_key(conn->b_crypto)) {
Nick Mathewson's avatar
src/or    
Nick Mathewson committed
299
    log(LOG_ERR,"Cannot generate a secure symmetric key.");
Roger Dingledine's avatar
Roger Dingledine committed
300
301
    return -1;
  }
Nick Mathewson's avatar
src/or    
Nick Mathewson committed
302
  log(LOG_DEBUG,"or_handshake_client_send_auth() : Generated symmetric keys.");
Roger Dingledine's avatar
Roger Dingledine committed
303
304

  /* generate first message */
305
  *(uint16_t*)buf = htons(HANDSHAKE_AS_OR);
306
307
308
309
310
  *(uint32_t*)(buf+FLAGS_LEN) = me.sin_addr.s_addr; /* local address, network order */
  *(uint16_t*)(buf+FLAGS_LEN+ADDR_LEN) = me.sin_port; /* local port, network order */
  *(uint32_t*)(buf+FLAGS_LEN+ADDR_LEN+PORT_LEN) = htonl(conn->addr); /* remote address */
  *(uint16_t*)(buf+FLAGS_LEN+ADDR_LEN+PORT_LEN+ADDR_LEN) = htons(conn->port); /* remote port */
  memcpy(buf+FLAGS_LEN+ADDR_LEN+PORT_LEN+ADDR_LEN+PORT_LEN,
311
         crypto_cipher_get_key(conn->f_crypto),16); /* keys */
312
  memcpy(buf+FLAGS_LEN+ADDR_LEN+PORT_LEN+ADDR_LEN+PORT_LEN+KEY_LEN,
313
         crypto_cipher_get_key(conn->b_crypto),16);
Roger Dingledine's avatar
Roger Dingledine committed
314
315
316
  log(LOG_DEBUG,"or_handshake_client_send_auth() : Generated first authentication message.");

  /* encrypt message */
317
  retval = crypto_pk_public_encrypt(conn->pkey, buf, sizeof(buf), cipher,RSA_PKCS1_PADDING);
Roger Dingledine's avatar
Roger Dingledine committed
318
319
  if (retval == -1) /* error */
  { 
320
    log(LOG_ERR,"Public-key encryption failed during authentication to %s:%u.",conn->address,conn->port);
321
    log(LOG_DEBUG,"or_handshake_client_send_auth() : Reason : %s.",crypto_perror());
Roger Dingledine's avatar
Roger Dingledine committed
322
323
324
325
326
327
    return -1;
  }
  log(LOG_DEBUG,"or_handshake_client_send_auth() : Encrypted authentication message.");

  /* send message */
  
328
  if(connection_write_to_buf(cipher, PKEY_LEN, conn) < 0) {
Roger Dingledine's avatar
Roger Dingledine committed
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
    log(LOG_DEBUG,"or_handshake_client_send_auth(): my outbuf is full. Oops.");
    return -1;
  }
  retval = connection_flush_buf(conn);
  if(retval < 0) {
    log(LOG_DEBUG,"or_handshake_client_send_auth(): bad socket while flushing.");
    return -1;
  }
  if(retval > 0) {
    /* still stuff on the buffer. */
    conn->state = OR_CONN_STATE_CLIENT_SENDING_AUTH;
    connection_watch_events(conn, POLLOUT | POLLIN);
    return 0;
  }

  /* it finished sending */
  log(LOG_DEBUG,"or_handshake_client_send_auth(): Finished sending authentication message.");
  conn->state = OR_CONN_STATE_CLIENT_AUTH_WAIT;
  connection_watch_events(conn, POLLIN);
  return 0;

}

352
353
static int 
or_handshake_client_process_auth(connection_t *conn) {
354
  char buf[128]; /* only 52 of this is expected to be used */
Roger Dingledine's avatar
Roger Dingledine committed
355
356
  char cipher[128];
  int retval;
357
  struct sockaddr_in me; /* my router identity */
Roger Dingledine's avatar
Roger Dingledine committed
358
359
360

  assert(conn);

361
362
363
  if(learn_my_address(&me) < 0)
    return -1;

Roger Dingledine's avatar
Roger Dingledine committed
364
365
366
  if(conn->inbuf_datalen < 128) /* entire response available? */
    return 0; /* not yet */

367
  connection_fetch_from_buf(cipher,128,conn);
Roger Dingledine's avatar
Roger Dingledine committed
368
369
370
  log(LOG_DEBUG,"or_handshake_client_process_auth() : Received auth.");

  /* decrypt response */
371
  retval = crypto_pk_private_decrypt(get_privatekey(), cipher, 128, buf, RSA_PKCS1_PADDING);
Roger Dingledine's avatar
Roger Dingledine committed
372
373
374
  if (retval == -1)
  { 
    log(LOG_ERR,"Public-key decryption failed during authentication to %s:%u.",
375
        conn->address,conn->port);
Roger Dingledine's avatar
Roger Dingledine committed
376
    log(LOG_DEBUG,"or_handshake_client_process_auth() : Reason : %s.",
377
        crypto_perror());
Roger Dingledine's avatar
Roger Dingledine committed
378
379
    return -1;
  }
380
  else if (retval != 52)
Roger Dingledine's avatar
Roger Dingledine committed
381
  { 
Roger Dingledine's avatar
Roger Dingledine committed
382
    log(LOG_ERR,"client_process_auth: incorrect response from router %s:%u.",
383
        conn->address,conn->port);
Roger Dingledine's avatar
Roger Dingledine committed
384
385
386
387
    return -1;
  }
  log(LOG_DEBUG,"or_handshake_client_process_auth() : Decrypted response.");
  /* check validity */
Roger Dingledine's avatar
Roger Dingledine committed
388
389
  if ( (*(uint32_t*)buf != me.sin_addr.s_addr) || /* local address, network order */
       (*(uint16_t*)(buf+4) != me.sin_port) || /* local port, network order */
390
       (ntohl(*(uint32_t*)(buf+6)) != conn->addr) || /* remote address */
Roger Dingledine's avatar
Roger Dingledine committed
391
392
393
394
       (ntohs(*(uint16_t*)(buf+10)) != conn->port) ) { /* remote port */
    log(LOG_ERR,"client_process_auth: Router %s:%u: bad address info.", conn->address,conn->port);
    return -1;
  }
395
396
  if ( (memcmp(crypto_cipher_get_key(conn->f_crypto), buf+12, 16)) ||/* keys */
       (memcmp(crypto_cipher_get_key(conn->b_crypto), buf+28, 16)) ) {
Roger Dingledine's avatar
Roger Dingledine committed
397
    log(LOG_ERR,"client_process_auth: Router %s:%u: bad key info.",conn->address,conn->port);
Roger Dingledine's avatar
Roger Dingledine committed
398
399
400
401
402
403
    return -1;
  }

  log(LOG_DEBUG,"or_handshake_client_process_auth() : Response valid.");

  /* reply is just local addr/port, remote addr/port, nonce */
404
  memcpy(buf+12, buf+44, 8);
Roger Dingledine's avatar
Roger Dingledine committed
405
406

  /* encrypt reply */
407
  retval = crypto_pk_public_encrypt(conn->pkey, buf, 20, cipher,RSA_PKCS1_PADDING);
Roger Dingledine's avatar
Roger Dingledine committed
408
409
  if (retval == -1) /* error */
  { 
410
    log(LOG_ERR,"Public-key encryption failed during authentication to %s:%u.",conn->address,conn->port);
411
    log(LOG_DEBUG,"or_handshake_client_process_auth() : Reason : %s.",crypto_perror());
Roger Dingledine's avatar
Roger Dingledine committed
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
    return -1;
  }

  /* send the message */

  if(connection_write_to_buf(cipher, 128, conn) < 0) {
    log(LOG_DEBUG,"or_handshake_client_process_auth(): my outbuf is full. Oops.");
    return -1;
  }
  retval = connection_flush_buf(conn);
  if(retval < 0) {
    log(LOG_DEBUG,"or_handshake_client_process_auth(): bad socket while flushing.");
    return -1;
  }
  if(retval > 0) {
    /* still stuff on the buffer. */
    conn->state = OR_CONN_STATE_CLIENT_SENDING_NONCE;
    connection_watch_events(conn, POLLOUT | POLLIN);
/*    return(connection_process_inbuf(conn)); process the rest of the inbuf */
    return 0;   
  }

  /* it finished sending */
  log(LOG_DEBUG,"or_handshake_client_process_auth(): Finished sending nonce.");
  conn_or_init_crypto(conn);
Roger Dingledine's avatar
Roger Dingledine committed
437
  connection_or_set_open(conn);
Roger Dingledine's avatar
Roger Dingledine committed
438
439
440
  return connection_process_inbuf(conn); /* process the rest of the inbuf */
}

Roger Dingledine's avatar
Roger Dingledine committed
441
442
/* auth handshake, as performed by OR *receiving* the connection */
static int or_handshake_server_process_auth(connection_t *conn) {
Roger Dingledine's avatar
Roger Dingledine committed
443
444
  int retval;

445
  char buf[128]; /* 50 of this is expected to be used for OR, 38 for OP */
Roger Dingledine's avatar
Roger Dingledine committed
446
447
  char cipher[128];

448
449
  unsigned char iv[16];

Roger Dingledine's avatar
Roger Dingledine committed
450
451
452
453
454
455
456
457
458
459
460
461
  uint32_t addr;
  uint16_t port;

  routerinfo_t *router;

  assert(conn);

  log(LOG_DEBUG,"or_handshake_server_process_auth() entered.");

  if(conn->inbuf_datalen < 128) /* entire response available? */
    return 0; /* not yet */  

462
  connection_fetch_from_buf(cipher,128,conn);
Roger Dingledine's avatar
Roger Dingledine committed
463
464
465
  log(LOG_DEBUG,"or_handshake_server_process_auth() : Received auth.");

  /* decrypt response */
466
  retval = crypto_pk_private_decrypt(get_privatekey(), cipher, 128, buf, RSA_PKCS1_PADDING);
467
  if (retval == -1) {
Roger Dingledine's avatar
Roger Dingledine committed
468
    log(LOG_ERR,"or_handshake_server_process_auth: Public-key decryption failed.");
Roger Dingledine's avatar
Roger Dingledine committed
469
    log(LOG_DEBUG,"or_handshake_server_process_auth() : Reason : %s.",
470
        crypto_perror());
Roger Dingledine's avatar
Roger Dingledine committed
471
472
473
    return -1;
  }

474
  if (retval == 46) {
Roger Dingledine's avatar
Roger Dingledine committed
475

476
477
478
479
480
    log(LOG_DEBUG,"or_handshake_server_process_auth(): Decrypted OR-style auth message.");
    if(ntohs(*(uint16_t*)buf) != HANDSHAKE_AS_OR) {
      log(LOG_DEBUG,"or_handshake_server_process_auth(): ...but wasn't labelled OR. Dropping.");
      return -1;
    }
Roger Dingledine's avatar
Roger Dingledine committed
481

482
483
484
    /* identify the router */
    addr = ntohl(*(uint32_t*)(buf+2)); /* save the IP address */
    port = ntohs(*(uint16_t*)(buf+6)); /* save the port */
Roger Dingledine's avatar
Roger Dingledine committed
485

486
487
488
489
490
491
492
    router = router_get_by_addr_port(addr,port);
    if (!router) {
      log(LOG_DEBUG,"or_handshake_server_process_auth() : unknown router '%s:%d'. Will drop.", conn->address, port);
      return -1;
    }
    log(LOG_DEBUG,"or_handshake_server_process_auth() : Router identified as %s:%u.",
        router->address,router->or_port);
Roger Dingledine's avatar
Roger Dingledine committed
493

494
495
496
497
    if(connection_exact_get_by_addr_port(addr,port)) {
      log(LOG_DEBUG,"or_handshake_server_process_auth(): That router is already connected. Dropping.");
      return -1;
    }
Roger Dingledine's avatar
Roger Dingledine committed
498

499
500
501
    /* save keys */
    crypto_cipher_set_key(conn->b_crypto,buf+14);
    crypto_cipher_set_key(conn->f_crypto,buf+30);
Roger Dingledine's avatar
Roger Dingledine committed
502

503
    conn->bandwidth = router->bandwidth;
Roger Dingledine's avatar
Roger Dingledine committed
504

505
506
507
    /* copy all relevant info to conn */
    conn->addr = router->addr, conn->port = router->or_port;
    conn->pkey = crypto_pk_dup_key(router->pkey);
508
509
    if(conn->address)
      free(conn->address);
510
    conn->address = strdup(router->address);
Roger Dingledine's avatar
Roger Dingledine committed
511

512
    /* generate a nonce */
513
    retval = crypto_rand(8, conn->nonce);
514
515
516
517
518
519
    if (retval) { /* error */
      log(LOG_ERR,"Cannot generate a nonce.");
      return -1;
    }
    log(LOG_DEBUG,"or_handshake_server_process_auth(): Nonce generated.");

Roger Dingledine's avatar
Roger Dingledine committed
520
    memmove(buf, buf+2, 44);
521
    memcpy(buf+44,conn->nonce,8); /* append the nonce to the end of the message */
522
523

    /* encrypt message */
524
    retval = crypto_pk_public_encrypt(conn->pkey, buf, 52, cipher,RSA_PKCS1_PADDING);
525
526
527
528
529
530
    if (retval == -1) { /* error */
      log(LOG_ERR,"Public-key encryption failed during authentication to %s:%u.",conn->address,conn->port);
      log(LOG_DEBUG,"or_handshake_server_process_auth() : Reason : %s.",crypto_perror());
      return -1;
    }
    log(LOG_DEBUG,"or_handshake_server_process_auth() : Reply encrypted.");
Roger Dingledine's avatar
Roger Dingledine committed
531

532
    /* send message */
Roger Dingledine's avatar
Roger Dingledine committed
533

534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
    if(connection_write_to_buf(cipher, 128, conn) < 0) {
      log(LOG_DEBUG,"or_handshake_server_process_auth(): my outbuf is full. Oops.");
      return -1;
    }
    retval = connection_flush_buf(conn);
    if(retval < 0) {
      log(LOG_DEBUG,"or_handshake_server_process_auth(): bad socket while flushing.");
      return -1;
    }
    if(retval > 0) {
      /* still stuff on the buffer. */
      conn->state = OR_CONN_STATE_SERVER_SENDING_AUTH;
      connection_watch_events(conn, POLLOUT | POLLIN);
      return 0;
    }

    /* it finished sending */
    log(LOG_DEBUG,"or_handshake_server_process_auth(): Finished sending auth.");
    conn->state = OR_CONN_STATE_SERVER_NONCE_WAIT;
    connection_watch_events(conn, POLLIN);
Roger Dingledine's avatar
Roger Dingledine committed
554
555
556
    return 0;
  }

557
  if(retval == 34) {
558
559
560
561
562
563
    log(LOG_DEBUG,"or_handshake_server_process_auth(): Decrypted OP-style auth message.");
    if(ntohs(*(uint16_t*)buf) != HANDSHAKE_AS_OP) {
      log(LOG_DEBUG,"or_handshake_server_process_auth(): ...but wasn't labelled OP. Dropping.");
      return -1;
    }

564
565
    crypto_cipher_set_key(conn->b_crypto, buf+2);
    crypto_cipher_set_key(conn->f_crypto, buf+18);
566
567
568
569
570
571
572
573
574
575
576
577
578

    memset(iv, 0, 16);
    crypto_cipher_set_iv(conn->b_crypto, iv);
    crypto_cipher_set_iv(conn->f_crypto, iv);

    crypto_cipher_encrypt_init_cipher(conn->b_crypto);
    crypto_cipher_decrypt_init_cipher(conn->f_crypto);

    conn->state = OR_CONN_STATE_OPEN;
    connection_watch_events(conn, POLLIN);

    return connection_process_inbuf(conn); /* in case they sent some cells along with the keys */
  }
Roger Dingledine's avatar
Roger Dingledine committed
579

580
581
  log(LOG_ERR,"or_handshake_server_process_auth(): received an incorrect authentication request.");
  return -1;
Roger Dingledine's avatar
Roger Dingledine committed
582
583
}

584
585
static int 
or_handshake_server_process_nonce(connection_t *conn) {
Roger Dingledine's avatar
Roger Dingledine committed
586
587
588
589

  char buf[128];
  char cipher[128];
  int retval;
590
  struct sockaddr_in me; /* my router identity */
Roger Dingledine's avatar
Roger Dingledine committed
591
592
593

  assert(conn);

594
595
596
  if(learn_my_address(&me) < 0)
    return -1;

Roger Dingledine's avatar
Roger Dingledine committed
597
598
599
  if(conn->inbuf_datalen < 128) /* entire response available? */
    return 0; /* not yet */

600
  connection_fetch_from_buf(cipher,128,conn);
Roger Dingledine's avatar
Roger Dingledine committed
601
602
603
  log(LOG_DEBUG,"or_handshake_server_process_nonce() : Received auth.");

  /* decrypt response */
604
  retval = crypto_pk_private_decrypt(get_privatekey(), cipher, 128, buf,RSA_PKCS1_PADDING);
Roger Dingledine's avatar
Roger Dingledine committed
605
606
607
  if (retval == -1)
  {
    log(LOG_ERR,"Public-key decryption failed during authentication to %s:%u.",
608
        conn->address,conn->port);
Roger Dingledine's avatar
Roger Dingledine committed
609
    log(LOG_DEBUG,"or_handshake_server_process_nonce() : Reason : %s.",
610
        crypto_perror());
Roger Dingledine's avatar
Roger Dingledine committed
611
612
613
614
    return -1;
  }
  else if (retval != 20)
  { 
Roger Dingledine's avatar
Roger Dingledine committed
615
    log(LOG_ERR,"server_process_nonce: incorrect response from router %s:%u.",
616
        conn->address,conn->port);
Roger Dingledine's avatar
Roger Dingledine committed
617
618
619
620
621
    return -1;
  }
  log(LOG_DEBUG,"or_handshake_server_process_nonce() : Response decrypted.");

  /* check validity */
622
  if ((ntohl(*(uint32_t*)buf) != conn->addr) || /* remote address */
623
      (ntohs(*(uint16_t*)(buf+4)) != conn->port) || /* remote port */ 
624
625
       (*(uint32_t*)(buf+6) != me.sin_addr.s_addr) || /* local address, network order */
       (*(uint16_t*)(buf+10) != me.sin_port) || /* local port, network order */
Roger Dingledine's avatar
Roger Dingledine committed
626
627
      (memcmp(conn->nonce,buf+12,8))) /* nonce */
  { 
Roger Dingledine's avatar
Roger Dingledine committed
628
    log(LOG_ERR,"server_process_nonce: Router %s:%u gave bad response.",conn->address,conn->port);
Roger Dingledine's avatar
Roger Dingledine committed
629
630
631
632
633
    return -1;
  }
  log(LOG_DEBUG,"or_handshake_server_process_nonce() : Response valid. Authentication complete.");

  conn_or_init_crypto(conn);
Roger Dingledine's avatar
Roger Dingledine committed
634
  connection_or_set_open(conn);
Roger Dingledine's avatar
Roger Dingledine committed
635
636
637
638
  return connection_process_inbuf(conn); /* process the rest of the inbuf */

}

639
/*********************/
Roger Dingledine's avatar
Roger Dingledine committed
640

641
642
643
644
645
646
647
648
649
650
static void 
connection_or_set_open(connection_t *conn) {
  conn->state = OR_CONN_STATE_OPEN;
  directory_set_dirty();
  connection_watch_events(conn, POLLIN);
}

static void 
conn_or_init_crypto(connection_t *conn) {
  unsigned char iv[16];
Roger Dingledine's avatar
Roger Dingledine committed
651

652
  assert(conn);
Roger Dingledine's avatar
Roger Dingledine committed
653

654
655
656
  memset((void *)iv, 0, 16);
  crypto_cipher_set_iv(conn->f_crypto, iv);
  crypto_cipher_set_iv(conn->b_crypto, iv);
Roger Dingledine's avatar
Roger Dingledine committed
657

658
659
660
  crypto_cipher_encrypt_init_cipher(conn->f_crypto);
  crypto_cipher_decrypt_init_cipher(conn->b_crypto);
    /* always encrypt with f, always decrypt with b */
Roger Dingledine's avatar
Roger Dingledine committed
661
}
662
#endif
Roger Dingledine's avatar
Roger Dingledine committed
663

664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
int connection_write_cell_to_buf(const cell_t *cellp, connection_t *conn) {
  char networkcell[CELL_NETWORK_SIZE];
  char *n = networkcell;

  cell_pack(n, cellp);
 
#ifndef USE_TLS
  if(connection_encrypt_cell(n,conn)<0) {
    return -1;
  }
#endif
 
  return connection_write_to_buf(n, CELL_NETWORK_SIZE, conn);
}

int connection_process_cell_from_inbuf(connection_t *conn) {
  /* check if there's a whole cell there.
   *    * if yes, pull it off, decrypt it if we're not doing TLS, and process it.
   *       */
#ifndef USE_TLS
  char networkcell[CELL_NETWORK_SIZE];
#endif
  char buf[CELL_NETWORK_SIZE];
//  int x;
  cell_t cell;
 
  if(conn->inbuf_datalen < CELL_NETWORK_SIZE) /* entire response available? */
    return 0; /* not yet */
 
#ifdef USE_TLS
  connection_fetch_from_buf(buf, CELL_NETWORK_SIZE, conn);
#else
  connection_fetch_from_buf(networkcell, CELL_NETWORK_SIZE, conn);
#if 0
  printf("Cell header crypttext: ");
  for(x=0;x<8;x++) {
    printf("%u ",crypted[x]);
  }
  printf("\n");
#endif
  /* decrypt */
  if(crypto_cipher_decrypt(conn->b_crypto, networkcell, CELL_NETWORK_SIZE, buf)) {
    log_fn(LOG_ERR,"Decryption failed, dropping.");
    return connection_process_inbuf(conn); /* process the remainder of the buffer */
  }
//  log_fn(LOG_DEBUG,"Cell decrypted (%d bytes).",outlen);
#if 0
  printf("Cell header plaintext: ");
  for(x=0;x<8;x++) {
    printf("%u ",outbuf[x]);
  }
  printf("\n");
#endif
#endif
 
  /* retrieve cell info from buf (create the host-order struct from the network-order string) */
  cell_unpack(&cell, buf);
 
//  log_fn(LOG_DEBUG,"Decrypted cell is of type %u (ACI %u).",cellp->command,cellp->aci);
  command_process_cell(&cell, conn);
 
  return connection_process_inbuf(conn); /* process the remainder of the buffer */
}

#ifndef USE_TLS
int connection_encrypt_cell(char *cellp, connection_t *conn) {
  char cryptcell[CELL_NETWORK_SIZE];
 
  assert(conn);
 
  if(crypto_cipher_encrypt(conn->f_crypto, cellp, CELL_NETWORK_SIZE, cryptcell)) {
    log(LOG_ERR,"Could not encrypt cell for connection %s:%u.",conn->address,conn->port);
    return -1;
  }

  memcpy(cellp,cryptcell,CELL_NETWORK_SIZE);
  return 0;
}
#endif

744
745


746
747
748
749
750
751
752
/*
  Local Variables:
  mode:c
  indent-tabs-mode:nil
  c-basic-offset:2
  End:
*/