Implement and use a generic auth. cookie initialization function.
[tor.git] / src / or / ext_orport.c
blob272fef4d1fe74dfc95dc184fd2de5c2907a60b6f
1 /* Copyright (c) 2012, The Tor Project, Inc. */
2 /* See LICENSE for licensing information */
4 /**
5 * \file ext_orport.c
6 * \brief Code implementing the Extended ORPort.
7 */
9 #define EXT_ORPORT_PRIVATE
10 #include "or.h"
11 #include "connection.h"
12 #include "connection_or.h"
13 #include "ext_orport.h"
14 #include "control.h"
15 #include "config.h"
16 #include "util.h"
17 #include "main.h"
19 /** Allocate and return a structure capable of holding an Extended
20 * ORPort message of body length <b>len</b>. */
21 ext_or_cmd_t *
22 ext_or_cmd_new(uint16_t len)
24 size_t size = STRUCT_OFFSET(ext_or_cmd_t, body) + len;
25 ext_or_cmd_t *cmd = tor_malloc(size);
26 cmd->len = len;
27 return cmd;
30 /** Deallocate the Extended ORPort message in <b>cmd</b>. */
31 void
32 ext_or_cmd_free(ext_or_cmd_t *cmd)
34 tor_free(cmd);
37 /** Get an Extended ORPort message from <b>conn</b>, and place it in
38 * <b>out</b>. Return -1 on fail, 0 if we need more data, and 1 if we
39 * successfully extracted an Extended ORPort command from the
40 * buffer. */
41 static int
42 connection_fetch_ext_or_cmd_from_buf(connection_t *conn, ext_or_cmd_t **out)
44 IF_HAS_BUFFEREVENT(conn, {
45 struct evbuffer *input = bufferevent_get_input(conn->bufev);
46 return fetch_ext_or_command_from_evbuffer(input, out);
47 }) ELSE_IF_NO_BUFFEREVENT {
48 return fetch_ext_or_command_from_buf(conn->inbuf, out);
52 /** Write an Extended ORPort message to <b>conn</b>. Use
53 * <b>command</b> as the command type, <b>bodylen</b> as the body
54 * length, and <b>body</b>, if it's present, as the body of the
55 * message. */
56 STATIC int
57 connection_write_ext_or_command(connection_t *conn,
58 uint16_t command,
59 const char *body,
60 size_t bodylen)
62 char header[4];
63 if (bodylen > UINT16_MAX)
64 return -1;
65 set_uint16(header, htons(command));
66 set_uint16(header+2, htons(bodylen));
67 connection_write_to_buf(header, 4, conn);
68 if (bodylen) {
69 tor_assert(body);
70 connection_write_to_buf(body, bodylen, conn);
72 return 0;
75 /** Transition from an Extended ORPort which accepts Extended ORPort
76 * messages, to an Extended ORport which accepts OR traffic. */
77 static void
78 connection_ext_or_transition(or_connection_t *conn)
80 tor_assert(conn->base_.type == CONN_TYPE_EXT_OR);
82 conn->base_.type = CONN_TYPE_OR;
83 control_event_or_conn_status(conn, OR_CONN_EVENT_NEW, 0);
84 connection_tls_start_handshake(conn, 1);
87 /** Length of authentication cookie. */
88 #define EXT_OR_PORT_AUTH_COOKIE_LEN 32
89 /** Length of the header of the cookie file. */
90 #define EXT_OR_PORT_AUTH_COOKIE_HEADER_LEN 32
91 /** Static cookie file header. */
92 #define EXT_OR_PORT_AUTH_COOKIE_HEADER "! Extended ORPort Auth Cookie !\x0a"
93 /** Length of safe-cookie protocol hashes. */
94 #define EXT_OR_PORT_AUTH_HASH_LEN DIGEST256_LEN
95 /** Length of safe-cookie protocol nonces. */
96 #define EXT_OR_PORT_AUTH_NONCE_LEN 32
97 /** Safe-cookie protocol constants. */
98 #define EXT_OR_PORT_AUTH_SERVER_TO_CLIENT_CONST \
99 "ExtORPort authentication server-to-client hash"
100 #define EXT_OR_PORT_AUTH_CLIENT_TO_SERVER_CONST \
101 "ExtORPort authentication client-to-server hash"
103 /* Code to indicate cookie authentication */
104 #define EXT_OR_AUTHTYPE_SAFECOOKIE 0x01
106 /** If true, we've set ext_or_auth_cookie to a secret code and stored
107 * it to disk. */
108 STATIC int ext_or_auth_cookie_is_set = 0;
109 /** If ext_or_auth_cookie_is_set, a secret cookie that we've stored to disk
110 * and which we're using to authenticate controllers. (If the controller can
111 * read it off disk, it has permission to connect.) */
112 STATIC uint8_t *ext_or_auth_cookie = NULL;
114 /** Helper: Return a newly allocated string containing a path to the
115 * file where we store our authentication cookie. */
116 char *
117 get_ext_or_auth_cookie_file_name(void)
119 const or_options_t *options = get_options();
120 if (options->ExtORPortCookieAuthFile &&
121 strlen(options->ExtORPortCookieAuthFile)) {
122 return tor_strdup(options->ExtORPortCookieAuthFile);
123 } else {
124 return get_datadir_fname("extended_orport_auth_cookie");
128 /* Initialize the cookie-based authentication system of the
129 * Extended ORPort. If <b>is_enabled</b> is 0, then disable the cookie
130 * authentication system. */
132 init_ext_or_cookie_authentication(int is_enabled)
134 char *fname = NULL;
135 int retval;
137 if (!is_enabled) {
138 ext_or_auth_cookie_is_set = 0;
139 return 0;
142 fname = get_ext_or_auth_cookie_file_name();
143 retval = init_cookie_authentication(fname, EXT_OR_PORT_AUTH_COOKIE_HEADER,
144 EXT_OR_PORT_AUTH_COOKIE_HEADER_LEN,
145 &ext_or_auth_cookie,
146 &ext_or_auth_cookie_is_set);
147 tor_free(fname);
148 return retval;
151 /** Read data from <b>conn</b> and see if the client sent us the
152 * authentication type that she prefers to use in this session.
154 * Return -1 if we received corrupted data or if we don't support the
155 * authentication type. Return 0 if we need more data in
156 * <b>conn</b>. Return 1 if the authentication type negotiation was
157 * successful. */
158 static int
159 connection_ext_or_auth_neg_auth_type(connection_t *conn)
161 char authtype[1] = {0};
163 if (connection_get_inbuf_len(conn) < 1)
164 return 0;
166 if (connection_fetch_from_buf(authtype, 1, conn) < 0)
167 return -1;
169 log_debug(LD_GENERAL, "Client wants us to use %d auth type", authtype[0]);
170 if (authtype[0] != EXT_OR_AUTHTYPE_SAFECOOKIE) {
171 /* '1' is the only auth type supported atm */
172 return -1;
175 conn->state = EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_NONCE;
176 return 1;
179 /** DOCDOC */
180 STATIC int
181 handle_client_auth_nonce(const char *client_nonce, size_t client_nonce_len,
182 char **client_hash_out,
183 char **reply_out, size_t *reply_len_out)
185 char server_hash[EXT_OR_PORT_AUTH_HASH_LEN] = {0};
186 char server_nonce[EXT_OR_PORT_AUTH_NONCE_LEN] = {0};
187 char *reply;
188 size_t reply_len;
190 if (client_nonce_len != EXT_OR_PORT_AUTH_NONCE_LEN)
191 return -1;
193 /* Get our nonce */
194 if (crypto_rand(server_nonce, EXT_OR_PORT_AUTH_NONCE_LEN) < 0)
195 return -1;
197 { /* set up macs */
198 size_t hmac_s_msg_len = strlen(EXT_OR_PORT_AUTH_SERVER_TO_CLIENT_CONST) +
199 2*EXT_OR_PORT_AUTH_NONCE_LEN;
200 size_t hmac_c_msg_len = strlen(EXT_OR_PORT_AUTH_CLIENT_TO_SERVER_CONST) +
201 2*EXT_OR_PORT_AUTH_NONCE_LEN;
203 char *hmac_s_msg = tor_malloc_zero(hmac_s_msg_len);
204 char *hmac_c_msg = tor_malloc_zero(hmac_c_msg_len);
205 char *correct_client_hash = tor_malloc_zero(EXT_OR_PORT_AUTH_HASH_LEN);
207 memcpy(hmac_s_msg,
208 EXT_OR_PORT_AUTH_SERVER_TO_CLIENT_CONST,
209 strlen(EXT_OR_PORT_AUTH_SERVER_TO_CLIENT_CONST));
210 memcpy(hmac_s_msg + strlen(EXT_OR_PORT_AUTH_SERVER_TO_CLIENT_CONST),
211 client_nonce, EXT_OR_PORT_AUTH_NONCE_LEN);
212 memcpy(hmac_s_msg + strlen(EXT_OR_PORT_AUTH_SERVER_TO_CLIENT_CONST) +
213 EXT_OR_PORT_AUTH_NONCE_LEN,
214 server_nonce, EXT_OR_PORT_AUTH_NONCE_LEN);
216 memcpy(hmac_c_msg,
217 EXT_OR_PORT_AUTH_CLIENT_TO_SERVER_CONST,
218 strlen(EXT_OR_PORT_AUTH_CLIENT_TO_SERVER_CONST));
219 memcpy(hmac_c_msg + strlen(EXT_OR_PORT_AUTH_CLIENT_TO_SERVER_CONST),
220 client_nonce, EXT_OR_PORT_AUTH_NONCE_LEN);
221 memcpy(hmac_c_msg + strlen(EXT_OR_PORT_AUTH_CLIENT_TO_SERVER_CONST) +
222 EXT_OR_PORT_AUTH_NONCE_LEN,
223 server_nonce, EXT_OR_PORT_AUTH_NONCE_LEN);
225 crypto_hmac_sha256(server_hash,
226 (char*)ext_or_auth_cookie,
227 EXT_OR_PORT_AUTH_COOKIE_LEN,
228 hmac_s_msg,
229 hmac_s_msg_len);
231 crypto_hmac_sha256(correct_client_hash,
232 (char*)ext_or_auth_cookie,
233 EXT_OR_PORT_AUTH_COOKIE_LEN,
234 hmac_c_msg,
235 hmac_c_msg_len);
237 /* Store the client hash we generated. We will need to compare it
238 with the hash sent by the client. */
239 *client_hash_out = correct_client_hash;
241 memwipe(hmac_s_msg, 0, hmac_s_msg_len);
242 memwipe(hmac_c_msg, 0, hmac_c_msg_len);
244 tor_free(hmac_s_msg);
245 tor_free(hmac_c_msg);
248 { /* debug logging */ /* XXX disable this codepath if not logging on debug?*/
249 char server_hash_encoded[(2*EXT_OR_PORT_AUTH_HASH_LEN) + 1];
250 char server_nonce_encoded[(2*EXT_OR_PORT_AUTH_NONCE_LEN) + 1];
251 char client_nonce_encoded[(2*EXT_OR_PORT_AUTH_NONCE_LEN) + 1];
253 base16_encode(server_hash_encoded, sizeof(server_hash_encoded),
254 server_hash, sizeof(server_hash));
255 base16_encode(server_nonce_encoded, sizeof(server_nonce_encoded),
256 server_nonce, sizeof(server_nonce));
257 base16_encode(client_nonce_encoded, sizeof(client_nonce_encoded),
258 client_nonce, sizeof(client_nonce));
260 log_debug(LD_GENERAL,
261 "server_hash: '%s'\nserver_nonce: '%s'\nclient_nonce: '%s'",
262 server_hash_encoded, server_nonce_encoded, client_nonce_encoded);
264 memwipe(server_hash_encoded, 0, sizeof(server_hash_encoded));
265 memwipe(server_nonce_encoded, 0, sizeof(server_nonce_encoded));
266 memwipe(client_nonce_encoded, 0, sizeof(client_nonce_encoded));
269 { /* write reply: (server_hash, server_nonce) */
271 reply_len = EXT_OR_PORT_AUTH_COOKIE_LEN+EXT_OR_PORT_AUTH_NONCE_LEN;
272 reply = tor_malloc_zero(reply_len);
273 memcpy(reply, server_hash, EXT_OR_PORT_AUTH_HASH_LEN);
274 memcpy(reply + EXT_OR_PORT_AUTH_HASH_LEN, server_nonce,
275 EXT_OR_PORT_AUTH_NONCE_LEN);
278 *reply_out = reply;
279 *reply_len_out = reply_len;
281 return 0;
284 /** Read the client's nonce out of <b>conn</b>, setup the safe-cookie
285 * crypto, and then send our own hash and nonce to the client
287 * Return -1 if there was an error; return 0 if we need more data in
288 * <b>conn</b>, and return 1 if we successfully retrieved the
289 * client's nonce and sent our own. */
290 static int
291 connection_ext_or_auth_handle_client_nonce(connection_t *conn)
293 char client_nonce[EXT_OR_PORT_AUTH_NONCE_LEN];
294 char *reply=NULL;
295 size_t reply_len=0;
297 if (!ext_or_auth_cookie_is_set) { /* this should not happen */
298 log_warn(LD_BUG, "Extended ORPort authentication cookie was not set. "
299 "That's weird since we should have done that on startup. "
300 "This might be a Tor bug, please file a bug report. ");
301 return -1;
304 if (connection_get_inbuf_len(conn) < EXT_OR_PORT_AUTH_NONCE_LEN)
305 return 0;
307 if (connection_fetch_from_buf(client_nonce,
308 EXT_OR_PORT_AUTH_NONCE_LEN, conn) < 0)
309 return -1;
311 /* We extract the ClientNonce from the received data, and use it to
312 calculate ServerHash and ServerNonce according to proposal 217.
314 We also calculate our own ClientHash value and save it in the
315 connection state. We validate it later against the ClientHash
316 sent by the client. */
317 if (handle_client_auth_nonce(client_nonce, sizeof(client_nonce),
318 &TO_OR_CONN(conn)->ext_or_auth_correct_client_hash,
319 &reply, &reply_len) < 0)
320 return -1;
322 connection_write_to_buf(reply, reply_len, conn);
324 memwipe(reply, 0, reply_len);
325 tor_free(reply);
327 log_debug(LD_GENERAL, "Got client nonce, and sent our own nonce and hash.");
329 conn->state = EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_HASH;
330 return 1;
333 #define connection_ext_or_auth_send_result_success(c) \
334 connection_ext_or_auth_send_result(c, 1)
335 #define connection_ext_or_auth_send_result_fail(c) \
336 connection_ext_or_auth_send_result(c, 0)
338 /** Send authentication results to <b>conn</b>. Successful results if
339 * <b>success</b> is set; failure results otherwise. */
340 static void
341 connection_ext_or_auth_send_result(connection_t *conn, int success)
343 if (success)
344 connection_write_to_buf("\x01", 1, conn);
345 else
346 connection_write_to_buf("\x00", 1, conn);
349 /** Receive the client's hash from <b>conn</b>, validate that it's
350 * correct, and then send the authentication results to the client.
352 * Return -1 if there was an error during validation; return 0 if we
353 * need more data in <b>conn</b>, and return 1 if we successfully
354 * validated the client's hash and sent a happy authentication
355 * result. */
356 static int
357 connection_ext_or_auth_handle_client_hash(connection_t *conn)
359 char provided_client_hash[EXT_OR_PORT_AUTH_HASH_LEN] = {0};
361 if (connection_get_inbuf_len(conn) < EXT_OR_PORT_AUTH_HASH_LEN)
362 return 0;
364 if (connection_fetch_from_buf(provided_client_hash,
365 EXT_OR_PORT_AUTH_HASH_LEN, conn) < 0)
366 return -1;
368 if (tor_memneq(TO_OR_CONN(conn)->ext_or_auth_correct_client_hash,
369 provided_client_hash, EXT_OR_PORT_AUTH_HASH_LEN)) {
370 log_warn(LD_GENERAL, "Incorrect client hash. Authentication failed.");
371 connection_ext_or_auth_send_result_fail(conn);
372 return -1;
375 log_debug(LD_GENERAL, "Got client's hash and it was legit.");
377 /* send positive auth result */
378 connection_ext_or_auth_send_result_success(conn);
379 conn->state = EXT_OR_CONN_STATE_OPEN;
380 return 1;
383 /** Handle data from <b>or_conn</b> received on Extended ORPort.
384 * Return -1 on error. 0 on unsufficient data. 1 on correct. */
385 static int
386 connection_ext_or_auth_process_inbuf(or_connection_t *or_conn)
388 connection_t *conn = TO_CONN(or_conn);
390 /* State transitions of the Extended ORPort authentication protocol:
392 EXT_OR_CONN_STATE_AUTH_WAIT_AUTH_TYPE (start state) ->
393 EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_NONCE ->
394 EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_HASH ->
395 EXT_OR_CONN_STATE_OPEN
397 During EXT_OR_CONN_STATE_OPEN, data is handled by
398 connection_ext_or_process_inbuf().
401 switch (conn->state) { /* Functionify */
402 case EXT_OR_CONN_STATE_AUTH_WAIT_AUTH_TYPE:
403 return connection_ext_or_auth_neg_auth_type(conn);
405 case EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_NONCE:
406 return connection_ext_or_auth_handle_client_nonce(conn);
408 case EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_HASH:
409 return connection_ext_or_auth_handle_client_hash(conn);
411 default:
412 log_warn(LD_BUG, "Encountered unexpected connection state %d while trying "
413 "to process Extended ORPort authentication data.", conn->state);
414 return -1;
418 /** Extended ORPort commands (Transport-to-Bridge) */
419 #define EXT_OR_CMD_TB_DONE 0x0000
420 #define EXT_OR_CMD_TB_USERADDR 0x0001
421 #define EXT_OR_CMD_TB_TRANSPORT 0x0002
423 /** Extended ORPort commands (Bridge-to-Transport) */
424 #define EXT_OR_CMD_BT_OKAY 0x1000
425 #define EXT_OR_CMD_BT_DENY 0x1001
426 #define EXT_OR_CMD_BT_CONTROL 0x1002
428 /** Process a USERADDR command from the Extended
429 * ORPort. <b>payload</b> is a payload of size <b>len</b>.
431 * If the USERADDR command was well formed, change the address of
432 * <b>conn</b> to the address on the USERADDR command.
434 * Return 0 on success and -1 on error. */
435 static int
436 connection_ext_or_handle_cmd_useraddr(connection_t *conn,
437 const char *payload, uint16_t len)
439 /* Copy address string. */
440 tor_addr_t addr;
441 uint16_t port;
442 char *addr_str;
443 char *address_part=NULL;
444 int res;
445 if (memchr(payload, '\0', len)) {
446 log_fn(LOG_PROTOCOL_WARN, LD_NET, "Unexpected NUL in ExtORPort UserAddr");
447 return -1;
450 addr_str = tor_memdup_nulterm(payload, len);
452 res = tor_addr_port_split(LOG_INFO, addr_str, &address_part, &port);
453 tor_free(addr_str);
454 if (res<0)
455 return -1;
457 res = tor_addr_parse(&addr, address_part);
458 tor_free(address_part);
459 if (res<0)
460 return -1;
462 { /* do some logging */
463 char *old_address = tor_dup_addr(&conn->addr);
464 char *new_address = tor_dup_addr(&addr);
466 log_debug(LD_NET, "Received USERADDR."
467 "We rewrite our address from '%s:%u' to '%s:%u'.",
468 safe_str(old_address), conn->port, safe_str(new_address), port);
470 tor_free(old_address);
471 tor_free(new_address);
474 /* record the address */
475 tor_addr_copy(&conn->addr, &addr);
476 conn->port = port;
478 return 0;
481 /** Process a TRANSPORT command from the Extended
482 * ORPort. <b>payload</b> is a payload of size <b>len</b>.
484 * If the TRANSPORT command was well formed, register the name of the
485 * transport on <b>conn</b>.
487 * Return 0 on success and -1 on error. */
488 static int
489 connection_ext_or_handle_cmd_transport(or_connection_t *conn,
490 const char *payload, uint16_t len)
492 char *transport_str;
493 if (memchr(payload, '\0', len)) {
494 log_fn(LOG_PROTOCOL_WARN, LD_NET, "Unexpected NUL in ExtORPort Transport");
495 return -1;
498 transport_str = tor_memdup_nulterm(payload, len);
500 /* Transport names MUST be C-identifiers. */
501 if (!string_is_C_identifier(transport_str)) {
502 tor_free(transport_str);
503 return -1;
506 /* If ext_or_transport is already occupied (because the PT sent two
507 * TRANSPORT commands), deallocate the old name and keep the new
508 * one */
509 if (conn->ext_or_transport)
510 tor_free(conn->ext_or_transport);
512 conn->ext_or_transport = transport_str;
513 return 0;
516 #define EXT_OR_CONN_STATE_IS_AUTHENTICATING(st) \
517 ((st) <= EXT_OR_CONN_STATE_AUTH_MAX)
519 /** Process Extended ORPort messages from <b>or_conn</b>. */
521 connection_ext_or_process_inbuf(or_connection_t *or_conn)
523 connection_t *conn = TO_CONN(or_conn);
524 ext_or_cmd_t *command;
525 int r;
527 /* DOCDOC Document the state machine and transitions in this function */
529 /* If we are still in the authentication stage, process traffic as
530 authentication data: */
531 while (EXT_OR_CONN_STATE_IS_AUTHENTICATING(conn->state)) {
532 log_debug(LD_GENERAL, "Got Extended ORPort authentication data (%u).",
533 (unsigned int) connection_get_inbuf_len(conn));
534 r = connection_ext_or_auth_process_inbuf(or_conn);
535 if (r < 0) {
536 connection_mark_for_close(conn);
537 return -1;
538 } else if (r == 0) {
539 return 0;
541 /* if r > 0, loop and process more data (if any). */
544 while (1) {
545 log_debug(LD_GENERAL, "Got Extended ORPort data.");
546 command = NULL;
547 r = connection_fetch_ext_or_cmd_from_buf(conn, &command);
548 if (r < 0)
549 goto err;
550 else if (r == 0)
551 return 0; /* need to wait for more data */
553 /* Got a command! */
554 tor_assert(command);
556 if (command->cmd == EXT_OR_CMD_TB_DONE) {
557 if (connection_get_inbuf_len(conn)) {
558 /* The inbuf isn't empty; the client is misbehaving. */
559 goto err;
562 log_debug(LD_NET, "Received DONE.");
564 /* If the transport proxy did not use the TRANSPORT command to
565 * specify the transport name, mark this as unknown transport. */
566 if (!or_conn->ext_or_transport) {
567 /* We write this string this way to avoid ??>, which is a C
568 * trigraph. */
569 or_conn->ext_or_transport = tor_strdup("<?" "?>");
572 connection_write_ext_or_command(conn, EXT_OR_CMD_BT_OKAY, NULL, 0);
574 /* can't transition immediately; need to flush first. */
575 conn->state = EXT_OR_CONN_STATE_FLUSHING;
576 connection_stop_reading(conn);
577 } else if (command->cmd == EXT_OR_CMD_TB_USERADDR) {
578 if (connection_ext_or_handle_cmd_useraddr(conn,
579 command->body, command->len) < 0)
580 goto err;
581 } else if (command->cmd == EXT_OR_CMD_TB_TRANSPORT) {
582 if (connection_ext_or_handle_cmd_transport(or_conn,
583 command->body, command->len) < 0)
584 goto err;
585 } else {
586 log_notice(LD_NET,"Got Extended ORPort command we don't regognize (%u).",
587 command->cmd);
590 ext_or_cmd_free(command);
593 return 0;
595 err:
596 ext_or_cmd_free(command);
597 connection_mark_for_close(conn);
598 return -1;
601 /** <b>conn</b> finished flushing Extended ORPort messages to the
602 * network, and is now ready to accept OR traffic. This function
603 * does the transition. */
605 connection_ext_or_finished_flushing(or_connection_t *conn)
607 if (conn->base_.state == EXT_OR_CONN_STATE_FLUSHING) {
608 connection_start_reading(TO_CONN(conn));
609 connection_ext_or_transition(conn);
611 return 0;
614 /** Initiate Extended ORPort authentication, by sending the list of
615 * supported authentication types to the client. */
617 connection_ext_or_start_auth(or_connection_t *or_conn)
619 connection_t *conn = TO_CONN(or_conn);
620 const uint8_t authtypes[] = {
621 /* We only support authtype '1' for now. */
622 EXT_OR_AUTHTYPE_SAFECOOKIE,
623 /* Marks the end of the list. */
627 log_debug(LD_GENERAL,
628 "ExtORPort authentication: Sending supported authentication types");
630 connection_write_to_buf((const char *)authtypes, sizeof(authtypes), conn);
631 conn->state = EXT_OR_CONN_STATE_AUTH_WAIT_AUTH_TYPE;
633 return 0;