summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--changes/bug23859
-rw-r--r--changes/bug41956
-rw-r--r--configure.in2
-rw-r--r--contrib/tor-mingw.nsi.in2
-rw-r--r--src/common/util.c161
-rw-r--r--src/common/util.h1
-rw-r--r--src/or/config.c2
-rw-r--r--src/or/rendclient.c46
-rw-r--r--src/or/rendservice.c506
-rw-r--r--src/or/rendservice.h2
-rw-r--r--src/or/rephist.c6
-rw-r--r--src/test/test_util.c75
-rw-r--r--src/win32/orconfig.h2
13 files changed, 577 insertions, 243 deletions
diff --git a/changes/bug2385 b/changes/bug2385
new file mode 100644
index 0000000000..5d571d910f
--- /dev/null
+++ b/changes/bug2385
@@ -0,0 +1,9 @@
+ o Minor features (security):
+ - Clear keys and key-derived material left on the stack in
+ rendservice.c and rendclient.c. This should make us more
+ forward-secure against cold-boot attacks and the like. Fix for
+ bug 2385.
+
+ - Check return value of crypto_pk_write_private_key_to_string() in
+ end_service_load_keys(). This should make us more forward-secure
+ against cold-boot attacks and the like. Fix for bug 2385.
diff --git a/changes/bug4195 b/changes/bug4195
new file mode 100644
index 0000000000..2e7a724871
--- /dev/null
+++ b/changes/bug4195
@@ -0,0 +1,6 @@
+ o Minor features:
+ - Enhance our internal sscanf replacement so that we can eliminate
+ the last remaining uses of the system sscanf. (Though those uses
+ of sscanf were safe, sscanf itself is generally error prone, so
+ we want to eliminate when we can.) Fixes ticket 4195 and Coverity
+ CID 448.
diff --git a/configure.in b/configure.in
index 556bb661a1..148edd4d69 100644
--- a/configure.in
+++ b/configure.in
@@ -4,7 +4,7 @@ dnl Copyright (c) 2007-2012, The Tor Project, Inc.
dnl See LICENSE for licensing information
AC_INIT
-AM_INIT_AUTOMAKE(tor, 0.2.3.17-beta-dev)
+AM_INIT_AUTOMAKE(tor, 0.2.4.0-alpha-dev)
AM_CONFIG_HEADER(orconfig.h)
AC_CANONICAL_HOST
diff --git a/contrib/tor-mingw.nsi.in b/contrib/tor-mingw.nsi.in
index 6643925cef..2d6d1dbe53 100644
--- a/contrib/tor-mingw.nsi.in
+++ b/contrib/tor-mingw.nsi.in
@@ -8,7 +8,7 @@
!include "LogicLib.nsh"
!include "FileFunc.nsh"
!insertmacro GetParameters
-!define VERSION "0.2.3.17-beta-dev"
+!define VERSION "0.2.4.0-alpha-dev"
!define INSTALLER "tor-${VERSION}-win32.exe"
!define WEBSITE "https://www.torproject.org/"
!define LICENSE "LICENSE"
diff --git a/src/common/util.c b/src/common/util.c
index 51d932146d..099cdd304c 100644
--- a/src/common/util.c
+++ b/src/common/util.c
@@ -655,6 +655,16 @@ fast_memcmpstart(const void *mem, size_t memlen,
return fast_memcmp(mem, prefix, plen);
}
+/** Given a nul-terminated string s, set every character before the nul
+ * to zero. */
+void
+tor_strclear(char *s)
+{
+ while (*s) {
+ *s++ = '\0';
+ }
+}
+
/** Return a pointer to the first char of s that is not whitespace and
* not a comment, or to the terminating NUL if no such character exists.
*/
@@ -2684,9 +2694,9 @@ digit_to_num(char d)
* success, store the result in <b>out</b>, advance bufp to the next
* character, and return 0. On failure, return -1. */
static int
-scan_unsigned(const char **bufp, unsigned *out, int width, int base)
+scan_unsigned(const char **bufp, unsigned long *out, int width, int base)
{
- unsigned result = 0;
+ unsigned long result = 0;
int scanned_so_far = 0;
const int hex = base==16;
tor_assert(base == 10 || base == 16);
@@ -2698,8 +2708,8 @@ scan_unsigned(const char **bufp, unsigned *out, int width, int base)
while (**bufp && (hex?TOR_ISXDIGIT(**bufp):TOR_ISDIGIT(**bufp))
&& scanned_so_far < width) {
int digit = hex?hex_decode_digit(*(*bufp)++):digit_to_num(*(*bufp)++);
- unsigned new_result = result * base + digit;
- if (new_result > UINT32_MAX || new_result < result)
+ unsigned long new_result = result * base + digit;
+ if (new_result < result)
return -1; /* over/underflow. */
result = new_result;
++scanned_so_far;
@@ -2712,6 +2722,89 @@ scan_unsigned(const char **bufp, unsigned *out, int width, int base)
return 0;
}
+/** Helper: Read an signed int from *<b>bufp</b> of up to <b>width</b>
+ * characters. (Handle arbitrary width if <b>width</b> is less than 0.) On
+ * success, store the result in <b>out</b>, advance bufp to the next
+ * character, and return 0. On failure, return -1. */
+static int
+scan_signed(const char **bufp, long *out, int width)
+{
+ int neg = 0;
+ unsigned long result = 0;
+
+ if (!bufp || !*bufp || !out)
+ return -1;
+ if (width<0)
+ width=MAX_SCANF_WIDTH;
+
+ if (**bufp == '-') {
+ neg = 1;
+ ++*bufp;
+ --width;
+ }
+
+ if (scan_unsigned(bufp, &result, width, 10) < 0)
+ return -1;
+
+ if (neg) {
+ if (result > ((unsigned long)LONG_MAX) + 1)
+ return -1; /* Underflow */
+ *out = -(long)result;
+ } else {
+ if (result > LONG_MAX)
+ return -1; /* Overflow */
+ *out = (long)result;
+ }
+
+ return 0;
+}
+
+/** Helper: Read a decimal-formatted double from *<b>bufp</b> of up to
+ * <b>width</b> characters. (Handle arbitrary width if <b>width</b> is less
+ * than 0.) On success, store the result in <b>out</b>, advance bufp to the
+ * next character, and return 0. On failure, return -1. */
+static int
+scan_double(const char **bufp, double *out, int width)
+{
+ int neg = 0;
+ double result = 0;
+ int scanned_so_far = 0;
+
+ if (!bufp || !*bufp || !out)
+ return -1;
+ if (width<0)
+ width=MAX_SCANF_WIDTH;
+
+ if (**bufp == '-') {
+ neg = 1;
+ ++*bufp;
+ }
+
+ while (**bufp && TOR_ISDIGIT(**bufp) && scanned_so_far < width) {
+ const int digit = digit_to_num(*(*bufp)++);
+ result = result * 10 + digit;
+ ++scanned_so_far;
+ }
+ if (**bufp == '.') {
+ double fracval = 0, denominator = 1;
+ ++*bufp;
+ ++scanned_so_far;
+ while (**bufp && TOR_ISDIGIT(**bufp) && scanned_so_far < width) {
+ const int digit = digit_to_num(*(*bufp)++);
+ fracval = fracval * 10 + digit;
+ denominator *= 10;
+ ++scanned_so_far;
+ }
+ result += fracval / denominator;
+ }
+
+ if (!scanned_so_far) /* No actual digits scanned */
+ return -1;
+
+ *out = neg ? -result : result;
+ return 0;
+}
+
/** Helper: copy up to <b>width</b> non-space characters from <b>bufp</b> to
* <b>out</b>. Make sure <b>out</b> is nul-terminated. Advance <b>bufp</b>
* to the next non-space character or the EOS. */
@@ -2748,6 +2841,7 @@ tor_vsscanf(const char *buf, const char *pattern, va_list ap)
}
} else {
int width = -1;
+ int longmod = 0;
++pattern;
if (TOR_ISDIGIT(*pattern)) {
width = digit_to_num(*pattern++);
@@ -2760,17 +2854,57 @@ tor_vsscanf(const char *buf, const char *pattern, va_list ap)
if (!width) /* No zero-width things. */
return -1;
}
+ if (*pattern == 'l') {
+ longmod = 1;
+ ++pattern;
+ }
if (*pattern == 'u' || *pattern == 'x') {
- unsigned *u = va_arg(ap, unsigned *);
+ unsigned long u;
const int base = (*pattern == 'u') ? 10 : 16;
if (!*buf)
return n_matched;
- if (scan_unsigned(&buf, u, width, base)<0)
+ if (scan_unsigned(&buf, &u, width, base)<0)
+ return n_matched;
+ if (longmod) {
+ unsigned long *out = va_arg(ap, unsigned long *);
+ *out = u;
+ } else {
+ unsigned *out = va_arg(ap, unsigned *);
+ if (u > UINT_MAX)
+ return n_matched;
+ *out = u;
+ }
+ ++pattern;
+ ++n_matched;
+ } else if (*pattern == 'f') {
+ double *d = va_arg(ap, double *);
+ if (!longmod)
+ return -1; /* float not supported */
+ if (!*buf)
+ return n_matched;
+ if (scan_double(&buf, d, width)<0)
return n_matched;
++pattern;
++n_matched;
+ } else if (*pattern == 'd') {
+ long lng=0;
+ if (scan_signed(&buf, &lng, width)<0)
+ return n_matched;
+ if (longmod) {
+ long *out = va_arg(ap, long *);
+ *out = lng;
+ } else {
+ int *out = va_arg(ap, int *);
+ if (lng < INT_MIN || lng > INT_MAX)
+ return n_matched;
+ *out = (int)lng;
+ }
+ ++pattern;
+ ++n_matched;
} else if (*pattern == 's') {
char *s = va_arg(ap, char *);
+ if (longmod)
+ return -1;
if (width < 0)
return -1;
if (scan_string(&buf, s, width)<0)
@@ -2779,6 +2913,8 @@ tor_vsscanf(const char *buf, const char *pattern, va_list ap)
++n_matched;
} else if (*pattern == 'c') {
char *ch = va_arg(ap, char *);
+ if (longmod)
+ return -1;
if (width != -1)
return -1;
if (!*buf)
@@ -2789,6 +2925,8 @@ tor_vsscanf(const char *buf, const char *pattern, va_list ap)
} else if (*pattern == '%') {
if (*buf != '%')
return n_matched;
+ if (longmod)
+ return -1;
++buf;
++pattern;
} else {
@@ -2802,9 +2940,14 @@ tor_vsscanf(const char *buf, const char *pattern, va_list ap)
/** Minimal sscanf replacement: parse <b>buf</b> according to <b>pattern</b>
* and store the results in the corresponding argument fields. Differs from
- * sscanf in that it: Only handles %u, %x, %c and %Ns. Does not handle
- * arbitrarily long widths. %u and %x do not consume any space. Is
- * locale-independent. Returns -1 on malformed patterns.
+ * sscanf in that:
+ * <ul><li>It only handles %u, %lu, %x, %lx, %<NUM>s, %d, %ld, %lf, and %c.
+ * <li>It only handles decimal inputs for %lf. (12.3, not 1.23e1)
+ * <li>It does not handle arbitrarily long widths.
+ * <li>Numbers do not consume any space characters.
+ * <li>It is locale-independent.
+ * <li>%u and %x do not consume any space.
+ * <li>It returns -1 on malformed patterns.</ul>
*
* (As with other locale-independent functions, we need this to parse data that
* is in ASCII without worrying that the C library's locale-handling will make
diff --git a/src/common/util.h b/src/common/util.h
index a2ab0ccac8..4ab93164dc 100644
--- a/src/common/util.h
+++ b/src/common/util.h
@@ -188,6 +188,7 @@ int strcasecmpstart(const char *s1, const char *s2) ATTR_NONNULL((1,2));
int strcmpend(const char *s1, const char *s2) ATTR_NONNULL((1,2));
int strcasecmpend(const char *s1, const char *s2) ATTR_NONNULL((1,2));
int fast_memcmpstart(const void *mem, size_t memlen, const char *prefix);
+void tor_strclear(char *s);
void tor_strstrip(char *s, const char *strip) ATTR_NONNULL((1,2));
long tor_parse_long(const char *s, int base, long min,
diff --git a/src/or/config.c b/src/or/config.c
index d90e0fc996..042fc1aa3c 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -1548,7 +1548,7 @@ options_act(const or_options_t *old_options)
monitor_owning_controller_process(options->OwningControllerProcess);
/* reload keys as needed for rendezvous services. */
- if (rend_service_load_keys()<0) {
+ if (rend_service_load_all_keys()<0) {
log_warn(LD_GENERAL,"Error loading rendezvous service keys");
return -1;
}
diff --git a/src/or/rendclient.c b/src/or/rendclient.c
index 6c751be27d..5b3b92e406 100644
--- a/src/or/rendclient.c
+++ b/src/or/rendclient.c
@@ -132,6 +132,7 @@ rend_client_send_introduction(origin_circuit_t *introcirc,
crypt_path_t *cpath;
off_t dh_offset;
crypto_pk_t *intro_key = NULL;
+ int status = 0;
tor_assert(introcirc->_base.purpose == CIRCUIT_PURPOSE_C_INTRODUCING);
tor_assert(rendcirc->_base.purpose == CIRCUIT_PURPOSE_C_REND_READY);
@@ -161,7 +162,8 @@ rend_client_send_introduction(origin_circuit_t *introcirc,
}
}
- return -1;
+ status = -1;
+ goto cleanup;
}
/* first 20 bytes of payload are the hash of Bob's pk */
@@ -184,13 +186,16 @@ rend_client_send_introduction(origin_circuit_t *introcirc,
smartlist_len(entry->parsed->intro_nodes));
if (rend_client_reextend_intro_circuit(introcirc)) {
+ status = -2;
goto perm_err;
} else {
- return -1;
+ status = -1;
+ goto cleanup;
}
}
if (crypto_pk_get_digest(intro_key, payload)<0) {
log_warn(LD_BUG, "Internal error: couldn't hash public key.");
+ status = -2;
goto perm_err;
}
@@ -202,10 +207,12 @@ rend_client_send_introduction(origin_circuit_t *introcirc,
cpath->magic = CRYPT_PATH_MAGIC;
if (!(cpath->dh_handshake_state = crypto_dh_new(DH_TYPE_REND))) {
log_warn(LD_BUG, "Internal error: couldn't allocate DH.");
+ status = -2;
goto perm_err;
}
if (crypto_dh_generate_public(cpath->dh_handshake_state)<0) {
log_warn(LD_BUG, "Internal error: couldn't generate g^x.");
+ status = -2;
goto perm_err;
}
}
@@ -256,6 +263,7 @@ rend_client_send_introduction(origin_circuit_t *introcirc,
if (crypto_dh_get_public(cpath->dh_handshake_state, tmp+dh_offset,
DH_KEY_LEN)<0) {
log_warn(LD_BUG, "Internal error: couldn't extract g^x.");
+ status = -2;
goto perm_err;
}
@@ -269,6 +277,7 @@ rend_client_send_introduction(origin_circuit_t *introcirc,
PK_PKCS1_OAEP_PADDING, 0);
if (r<0) {
log_warn(LD_BUG,"Internal error: hybrid pk encrypt failed.");
+ status = -2;
goto perm_err;
}
@@ -288,7 +297,8 @@ rend_client_send_introduction(origin_circuit_t *introcirc,
introcirc->cpath->prev)<0) {
/* introcirc is already marked for close. leave rendcirc alone. */
log_warn(LD_BUG, "Couldn't send INTRODUCE1 cell");
- return -2;
+ status = -2;
+ goto cleanup;
}
/* Now, we wait for an ACK or NAK on this circuit. */
@@ -299,12 +309,17 @@ rend_client_send_introduction(origin_circuit_t *introcirc,
* state. */
introcirc->_base.timestamp_dirty = time(NULL);
- return 0;
+ goto cleanup;
+
perm_err:
if (!introcirc->_base.marked_for_close)
circuit_mark_for_close(TO_CIRCUIT(introcirc), END_CIRC_REASON_INTERNAL);
circuit_mark_for_close(TO_CIRCUIT(rendcirc), END_CIRC_REASON_INTERNAL);
- return -2;
+ cleanup:
+ memset(payload, 0, sizeof(payload));
+ memset(tmp, 0, sizeof(tmp));
+
+ return status;
}
/** Called when a rendezvous circuit is open; sends a establish
@@ -659,10 +674,17 @@ rend_client_refetch_v2_renddesc(const rend_data_t *rend_query)
time(NULL), chosen_replica) < 0) {
log_warn(LD_REND, "Internal error: Computing v2 rendezvous "
"descriptor ID did not succeed.");
- return;
+ /*
+ * Hmm, can this write anything to descriptor_id and still fail?
+ * Let's clear it just to be safe.
+ *
+ * From here on, any returns should goto done which clears
+ * descriptor_id so we don't leave key-derived material on the stack.
+ */
+ goto done;
}
if (directory_get_from_hs_dir(descriptor_id, rend_query) != 0)
- return; /* either success or failure, but we're done */
+ goto done; /* either success or failure, but we're done */
}
/* If we come here, there are no hidden service directories left. */
log_info(LD_REND, "Could not pick one of the responsible hidden "
@@ -670,6 +692,10 @@ rend_client_refetch_v2_renddesc(const rend_data_t *rend_query)
"we already tried them all unsuccessfully.");
/* Close pending connections. */
rend_client_desc_trynow(rend_query->onion_address);
+
+ done:
+ memset(descriptor_id, 0, sizeof(descriptor_id));
+
return;
}
@@ -1172,11 +1198,11 @@ rend_parse_service_authorization(const or_options_t *options,
strmap_t *parsed = strmap_new();
smartlist_t *sl = smartlist_new();
rend_service_authorization_t *auth = NULL;
+ char descriptor_cookie_tmp[REND_DESC_COOKIE_LEN+2];
+ char descriptor_cookie_base64ext[REND_DESC_COOKIE_LEN_BASE64+2+1];
for (line = options->HidServAuth; line; line = line->next) {
char *onion_address, *descriptor_cookie;
- char descriptor_cookie_tmp[REND_DESC_COOKIE_LEN+2];
- char descriptor_cookie_base64ext[REND_DESC_COOKIE_LEN_BASE64+2+1];
int auth_type_val = 0;
auth = NULL;
SMARTLIST_FOREACH(sl, char *, c, tor_free(c););
@@ -1253,6 +1279,8 @@ rend_parse_service_authorization(const or_options_t *options,
} else {
strmap_free(parsed, rend_service_authorization_strmap_item_free);
}
+ memset(descriptor_cookie_tmp, 0, sizeof(descriptor_cookie_tmp));
+ memset(descriptor_cookie_base64ext, 0, sizeof(descriptor_cookie_base64ext));
return res;
}
diff --git a/src/or/rendservice.c b/src/or/rendservice.c
index 6a51874699..8acc226e3a 100644
--- a/src/or/rendservice.c
+++ b/src/or/rendservice.c
@@ -31,6 +31,10 @@ static rend_intro_point_t *find_intro_point(origin_circuit_t *circ);
static int intro_point_accepted_intro_count(rend_intro_point_t *intro);
static int intro_point_should_expire_now(rend_intro_point_t *intro,
time_t now);
+struct rend_service_t;
+static int rend_service_load_keys(struct rend_service_t *s);
+static int rend_service_load_auth_keys(struct rend_service_t *s,
+ const char *hfname);
/** Represents the mapping from a virtual port of a rendezvous service to
* a real port on some IP.
@@ -135,7 +139,9 @@ rend_authorized_client_free(rend_authorized_client_t *client)
return;
if (client->client_key)
crypto_pk_free(client->client_key);
+ tor_strclear(client->client_name);
tor_free(client->client_name);
+ memset(client->descriptor_cookie, 0, sizeof(client->descriptor_cookie));
tor_free(client);
}
@@ -609,231 +615,273 @@ rend_service_update_descriptor(rend_service_t *service)
/** Load and/or generate private keys for all hidden services, possibly
* including keys for client authorization. Return 0 on success, -1 on
- * failure.
- */
+ * failure. */
int
-rend_service_load_keys(void)
+rend_service_load_all_keys(void)
{
- int r = 0;
- char fname[512];
- char buf[1500];
-
SMARTLIST_FOREACH_BEGIN(rend_service_list, rend_service_t *, s) {
if (s->private_key)
continue;
log_info(LD_REND, "Loading hidden-service keys from \"%s\"",
s->directory);
- /* Check/create directory */
- if (check_private_dir(s->directory, CPD_CREATE, get_options()->User) < 0)
+ if (rend_service_load_keys(s) < 0)
return -1;
+ } SMARTLIST_FOREACH_END(s);
+
+ return 0;
+}
+
+/** Load and/or generate private keys for the hidden service <b>s</b>,
+ * possibly including keys for client authorization. Return 0 on success, -1
+ * on failure. */
+static int
+rend_service_load_keys(rend_service_t *s)
+{
+ char fname[512];
+ char buf[128];
+
+ /* Check/create directory */
+ if (check_private_dir(s->directory, CPD_CREATE, get_options()->User) < 0)
+ return -1;
+
+ /* Load key */
+ if (strlcpy(fname,s->directory,sizeof(fname)) >= sizeof(fname) ||
+ strlcat(fname,PATH_SEPARATOR"private_key",sizeof(fname))
+ >= sizeof(fname)) {
+ log_warn(LD_CONFIG, "Directory name too long to store key file: \"%s\".",
+ s->directory);
+ return -1;
+ }
+ s->private_key = init_key_from_file(fname, 1, LOG_ERR);
+ if (!s->private_key)
+ return -1;
+
+ /* Create service file */
+ if (rend_get_service_id(s->private_key, s->service_id)<0) {
+ log_warn(LD_BUG, "Internal error: couldn't encode service ID.");
+ return -1;
+ }
+ if (crypto_pk_get_digest(s->private_key, s->pk_digest)<0) {
+ log_warn(LD_BUG, "Couldn't compute hash of public key.");
+ return -1;
+ }
+ if (strlcpy(fname,s->directory,sizeof(fname)) >= sizeof(fname) ||
+ strlcat(fname,PATH_SEPARATOR"hostname",sizeof(fname))
+ >= sizeof(fname)) {
+ log_warn(LD_CONFIG, "Directory name too long to store hostname file:"
+ " \"%s\".", s->directory);
+ return -1;
+ }
- /* Load key */
- if (strlcpy(fname,s->directory,sizeof(fname)) >= sizeof(fname) ||
- strlcat(fname,PATH_SEPARATOR"private_key",sizeof(fname))
- >= sizeof(fname)) {
- log_warn(LD_CONFIG, "Directory name too long to store key file: \"%s\".",
- s->directory);
+ tor_snprintf(buf, sizeof(buf),"%s.onion\n", s->service_id);
+ if (write_str_to_file(fname,buf,0)<0) {
+ log_warn(LD_CONFIG, "Could not write onion address to hostname file.");
+ memset(buf, 0, sizeof(buf));
+ return -1;
+ }
+ memset(buf, 0, sizeof(buf));
+
+ /* If client authorization is configured, load or generate keys. */
+ if (s->auth_type != REND_NO_AUTH) {
+ if (rend_service_load_auth_keys(s, fname) < 0)
return -1;
+ }
+
+ return 0;
+}
+
+/** Load and/or generate client authorization keys for the hidden service
+ * <b>s</b>, which stores its hostname in <b>hfname</b>. Return 0 on success,
+ * -1 on failure. */
+static int
+rend_service_load_auth_keys(rend_service_t *s, const char *hfname)
+{
+ int r = 0;
+ char cfname[512];
+ char *client_keys_str = NULL;
+ strmap_t *parsed_clients = strmap_new();
+ FILE *cfile, *hfile;
+ open_file_t *open_cfile = NULL, *open_hfile = NULL;
+ char extended_desc_cookie[REND_DESC_COOKIE_LEN+1];
+ char desc_cook_out[3*REND_DESC_COOKIE_LEN_BASE64+1];
+ char service_id[16+1];
+ char buf[1500];
+
+ /* Load client keys and descriptor cookies, if available. */
+ if (tor_snprintf(cfname, sizeof(cfname), "%s"PATH_SEPARATOR"client_keys",
+ s->directory)<0) {
+ log_warn(LD_CONFIG, "Directory name too long to store client keys "
+ "file: \"%s\".", s->directory);
+ goto err;
+ }
+ client_keys_str = read_file_to_str(cfname, RFTS_IGNORE_MISSING, NULL);
+ if (client_keys_str) {
+ if (rend_parse_client_keys(parsed_clients, client_keys_str) < 0) {
+ log_warn(LD_CONFIG, "Previously stored client_keys file could not "
+ "be parsed.");
+ goto err;
+ } else {
+ log_info(LD_CONFIG, "Parsed %d previously stored client entries.",
+ strmap_size(parsed_clients));
+ tor_free(client_keys_str);
}
- s->private_key = init_key_from_file(fname, 1, LOG_ERR);
- if (!s->private_key)
- return -1;
+ }
- /* Create service file */
- if (rend_get_service_id(s->private_key, s->service_id)<0) {
- log_warn(LD_BUG, "Internal error: couldn't encode service ID.");
- return -1;
+ /* Prepare client_keys and hostname files. */
+ if (!(cfile = start_writing_to_stdio_file(cfname,
+ OPEN_FLAGS_REPLACE | O_TEXT,
+ 0600, &open_cfile))) {
+ log_warn(LD_CONFIG, "Could not open client_keys file %s",
+ escaped(cfname));
+ goto err;
+ }
+
+ if (!(hfile = start_writing_to_stdio_file(hfname,
+ OPEN_FLAGS_REPLACE | O_TEXT,
+ 0600, &open_hfile))) {
+ log_warn(LD_CONFIG, "Could not open hostname file %s", escaped(hfname));
+ goto err;
+ }
+
+ /* Either use loaded keys for configured clients or generate new
+ * ones if a client is new. */
+ SMARTLIST_FOREACH_BEGIN(s->clients, rend_authorized_client_t *, client) {
+ rend_authorized_client_t *parsed =
+ strmap_get(parsed_clients, client->client_name);
+ int written;
+ size_t len;
+ /* Copy descriptor cookie from parsed entry or create new one. */
+ if (parsed) {
+ memcpy(client->descriptor_cookie, parsed->descriptor_cookie,
+ REND_DESC_COOKIE_LEN);
+ } else {
+ crypto_rand(client->descriptor_cookie, REND_DESC_COOKIE_LEN);
}
- if (crypto_pk_get_digest(s->private_key, s->pk_digest)<0) {
- log_warn(LD_BUG, "Couldn't compute hash of public key.");
- return -1;
+ if (base64_encode(desc_cook_out, 3*REND_DESC_COOKIE_LEN_BASE64+1,
+ client->descriptor_cookie,
+ REND_DESC_COOKIE_LEN) < 0) {
+ log_warn(LD_BUG, "Could not base64-encode descriptor cookie.");
+ goto err;
}
- if (strlcpy(fname,s->directory,sizeof(fname)) >= sizeof(fname) ||
- strlcat(fname,PATH_SEPARATOR"hostname",sizeof(fname))
- >= sizeof(fname)) {
- log_warn(LD_CONFIG, "Directory name too long to store hostname file:"
- " \"%s\".", s->directory);
- return -1;
+ /* Copy client key from parsed entry or create new one if required. */
+ if (parsed && parsed->client_key) {
+ client->client_key = crypto_pk_dup_key(parsed->client_key);
+ } else if (s->auth_type == REND_STEALTH_AUTH) {
+ /* Create private key for client. */
+ crypto_pk_t *prkey = NULL;
+ if (!(prkey = crypto_pk_new())) {
+ log_warn(LD_BUG,"Error constructing client key");
+ goto err;
+ }
+ if (crypto_pk_generate_key(prkey)) {
+ log_warn(LD_BUG,"Error generating client key");
+ crypto_pk_free(prkey);
+ goto err;
+ }
+ if (crypto_pk_check_key(prkey) <= 0) {
+ log_warn(LD_BUG,"Generated client key seems invalid");
+ crypto_pk_free(prkey);
+ goto err;
+ }
+ client->client_key = prkey;
}
- tor_snprintf(buf, sizeof(buf),"%s.onion\n", s->service_id);
- if (write_str_to_file(fname,buf,0)<0) {
- log_warn(LD_CONFIG, "Could not write onion address to hostname file.");
- return -1;
+ /* Add entry to client_keys file. */
+ desc_cook_out[strlen(desc_cook_out)-1] = '\0'; /* Remove newline. */
+ written = tor_snprintf(buf, sizeof(buf),
+ "client-name %s\ndescriptor-cookie %s\n",
+ client->client_name, desc_cook_out);
+ if (written < 0) {
+ log_warn(LD_BUG, "Could not write client entry.");
+ goto err;
}
-
- /* If client authorization is configured, load or generate keys. */
- if (s->auth_type != REND_NO_AUTH) {
- char *client_keys_str = NULL;
- strmap_t *parsed_clients = strmap_new();
- char cfname[512];
- FILE *cfile, *hfile;
- open_file_t *open_cfile = NULL, *open_hfile = NULL;
-
- /* Load client keys and descriptor cookies, if available. */
- if (tor_snprintf(cfname, sizeof(cfname), "%s"PATH_SEPARATOR"client_keys",
- s->directory)<0) {
- log_warn(LD_CONFIG, "Directory name too long to store client keys "
- "file: \"%s\".", s->directory);
+ if (client->client_key) {
+ char *client_key_out = NULL;
+ if (crypto_pk_write_private_key_to_string(client->client_key,
+ &client_key_out, &len) != 0) {
+ log_warn(LD_BUG, "Internal error: "
+ "crypto_pk_write_private_key_to_string() failed.");
goto err;
}
- client_keys_str = read_file_to_str(cfname, RFTS_IGNORE_MISSING, NULL);
- if (client_keys_str) {
- if (rend_parse_client_keys(parsed_clients, client_keys_str) < 0) {
- log_warn(LD_CONFIG, "Previously stored client_keys file could not "
- "be parsed.");
- goto err;
- } else {
- log_info(LD_CONFIG, "Parsed %d previously stored client entries.",
- strmap_size(parsed_clients));
- tor_free(client_keys_str);
- }
- }
-
- /* Prepare client_keys and hostname files. */
- if (!(cfile = start_writing_to_stdio_file(cfname,
- OPEN_FLAGS_REPLACE | O_TEXT,
- 0600, &open_cfile))) {
- log_warn(LD_CONFIG, "Could not open client_keys file %s",
- escaped(cfname));
+ if (rend_get_service_id(client->client_key, service_id)<0) {
+ log_warn(LD_BUG, "Internal error: couldn't encode service ID.");
+ /*
+ * len is string length, not buffer length, but last byte is NUL
+ * anyway.
+ */
+ memset(client_key_out, 0, len);
+ tor_free(client_key_out);
goto err;
}
- if (!(hfile = start_writing_to_stdio_file(fname,
- OPEN_FLAGS_REPLACE | O_TEXT,
- 0600, &open_hfile))) {
- log_warn(LD_CONFIG, "Could not open hostname file %s", escaped(fname));
+ written = tor_snprintf(buf + written, sizeof(buf) - written,
+ "client-key\n%s", client_key_out);
+ memset(client_key_out, 0, len);
+ tor_free(client_key_out);
+ if (written < 0) {
+ log_warn(LD_BUG, "Could not write client entry.");
goto err;
}
+ }
- /* Either use loaded keys for configured clients or generate new
- * ones if a client is new. */
- SMARTLIST_FOREACH_BEGIN(s->clients, rend_authorized_client_t *, client)
- {
- char desc_cook_out[3*REND_DESC_COOKIE_LEN_BASE64+1];
- char service_id[16+1];
- rend_authorized_client_t *parsed =
- strmap_get(parsed_clients, client->client_name);
- int written;
- size_t len;
- /* Copy descriptor cookie from parsed entry or create new one. */
- if (parsed) {
- memcpy(client->descriptor_cookie, parsed->descriptor_cookie,
- REND_DESC_COOKIE_LEN);
- } else {
- crypto_rand(client->descriptor_cookie, REND_DESC_COOKIE_LEN);
- }
- if (base64_encode(desc_cook_out, 3*REND_DESC_COOKIE_LEN_BASE64+1,
- client->descriptor_cookie,
- REND_DESC_COOKIE_LEN) < 0) {
- log_warn(LD_BUG, "Could not base64-encode descriptor cookie.");
- strmap_free(parsed_clients, rend_authorized_client_strmap_item_free);
- return -1;
- }
- /* Copy client key from parsed entry or create new one if required. */
- if (parsed && parsed->client_key) {
- client->client_key = crypto_pk_dup_key(parsed->client_key);
- } else if (s->auth_type == REND_STEALTH_AUTH) {
- /* Create private key for client. */
- crypto_pk_t *prkey = NULL;
- if (!(prkey = crypto_pk_new())) {
- log_warn(LD_BUG,"Error constructing client key");
- goto err;
- }
- if (crypto_pk_generate_key(prkey)) {
- log_warn(LD_BUG,"Error generating client key");
- crypto_pk_free(prkey);
- goto err;
- }
- if (crypto_pk_check_key(prkey) <= 0) {
- log_warn(LD_BUG,"Generated client key seems invalid");
- crypto_pk_free(prkey);
- goto err;
- }
- client->client_key = prkey;
- }
- /* Add entry to client_keys file. */
- desc_cook_out[strlen(desc_cook_out)-1] = '\0'; /* Remove newline. */
- written = tor_snprintf(buf, sizeof(buf),
- "client-name %s\ndescriptor-cookie %s\n",
- client->client_name, desc_cook_out);
- if (written < 0) {
- log_warn(LD_BUG, "Could not write client entry.");
- goto err;
- }
- if (client->client_key) {
- char *client_key_out = NULL;
- crypto_pk_write_private_key_to_string(client->client_key,
- &client_key_out, &len);
- if (rend_get_service_id(client->client_key, service_id)<0) {
- log_warn(LD_BUG, "Internal error: couldn't encode service ID.");
- tor_free(client_key_out);
- goto err;
- }
- written = tor_snprintf(buf + written, sizeof(buf) - written,
- "client-key\n%s", client_key_out);
- tor_free(client_key_out);
- if (written < 0) {
- log_warn(LD_BUG, "Could not write client entry.");
- goto err;
- }
- }
-
- if (fputs(buf, cfile) < 0) {
- log_warn(LD_FS, "Could not append client entry to file: %s",
- strerror(errno));
- goto err;
- }
-
- /* Add line to hostname file. */
- if (s->auth_type == REND_BASIC_AUTH) {
- /* Remove == signs (newline has been removed above). */
- desc_cook_out[strlen(desc_cook_out)-2] = '\0';
- tor_snprintf(buf, sizeof(buf),"%s.onion %s # client: %s\n",
- s->service_id, desc_cook_out, client->client_name);
- } else {
- char extended_desc_cookie[REND_DESC_COOKIE_LEN+1];
- memcpy(extended_desc_cookie, client->descriptor_cookie,
- REND_DESC_COOKIE_LEN);
- extended_desc_cookie[REND_DESC_COOKIE_LEN] =
- ((int)s->auth_type - 1) << 4;
- if (base64_encode(desc_cook_out, 3*REND_DESC_COOKIE_LEN_BASE64+1,
- extended_desc_cookie,
- REND_DESC_COOKIE_LEN+1) < 0) {
- log_warn(LD_BUG, "Could not base64-encode descriptor cookie.");
- goto err;
- }
- desc_cook_out[strlen(desc_cook_out)-3] = '\0'; /* Remove A= and
- newline. */
- tor_snprintf(buf, sizeof(buf),"%s.onion %s # client: %s\n",
- service_id, desc_cook_out, client->client_name);
- }
+ if (fputs(buf, cfile) < 0) {
+ log_warn(LD_FS, "Could not append client entry to file: %s",
+ strerror(errno));
+ goto err;
+ }
- if (fputs(buf, hfile)<0) {
- log_warn(LD_FS, "Could not append host entry to file: %s",
- strerror(errno));
- goto err;
- }
+ /* Add line to hostname file. */
+ if (s->auth_type == REND_BASIC_AUTH) {
+ /* Remove == signs (newline has been removed above). */
+ desc_cook_out[strlen(desc_cook_out)-2] = '\0';
+ tor_snprintf(buf, sizeof(buf),"%s.onion %s # client: %s\n",
+ s->service_id, desc_cook_out, client->client_name);
+ } else {
+ memcpy(extended_desc_cookie, client->descriptor_cookie,
+ REND_DESC_COOKIE_LEN);
+ extended_desc_cookie[REND_DESC_COOKIE_LEN] =
+ ((int)s->auth_type - 1) << 4;
+ if (base64_encode(desc_cook_out, 3*REND_DESC_COOKIE_LEN_BASE64+1,
+ extended_desc_cookie,
+ REND_DESC_COOKIE_LEN+1) < 0) {
+ log_warn(LD_BUG, "Could not base64-encode descriptor cookie.");
+ goto err;
}
- SMARTLIST_FOREACH_END(client);
+ desc_cook_out[strlen(desc_cook_out)-3] = '\0'; /* Remove A= and
+ newline. */
+ tor_snprintf(buf, sizeof(buf),"%s.onion %s # client: %s\n",
+ service_id, desc_cook_out, client->client_name);
+ }
- goto done;
- err:
- r = -1;
- done:
- tor_free(client_keys_str);
- strmap_free(parsed_clients, rend_authorized_client_strmap_item_free);
- if (r<0) {
- if (open_cfile)
- abort_writing_to_file(open_cfile);
- if (open_hfile)
- abort_writing_to_file(open_hfile);
- return r;
- } else {
- finish_writing_to_file(open_cfile);
- finish_writing_to_file(open_hfile);
- }
+ if (fputs(buf, hfile)<0) {
+ log_warn(LD_FS, "Could not append host entry to file: %s",
+ strerror(errno));
+ goto err;
}
- } SMARTLIST_FOREACH_END(s);
+ } SMARTLIST_FOREACH_END(client);
+
+ finish_writing_to_file(open_cfile);
+ finish_writing_to_file(open_hfile);
+
+ goto done;
+ err:
+ r = -1;
+ if (open_cfile)
+ abort_writing_to_file(open_cfile);
+ if (open_hfile)
+ abort_writing_to_file(open_hfile);
+ done:
+ tor_strclear(client_keys_str);
+ tor_free(client_keys_str);
+ strmap_free(parsed_clients, rend_authorized_client_strmap_item_free);
+
+ memset(cfname, 0, sizeof(cfname));
+
+ /* Clear stack buffers that held key-derived material. */
+ memset(buf, 0, sizeof(buf));
+ memset(desc_cook_out, 0, sizeof(desc_cook_out));
+ memset(service_id, 0, sizeof(service_id));
+ memset(extended_desc_cookie, 0, sizeof(extended_desc_cookie));
+
return r;
}
@@ -1038,6 +1086,7 @@ int
rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
size_t request_len)
{
+ int status = 0;
char *ptr, *r_cookie;
extend_info_t *extend_info = NULL;
char buf[RELAY_PAYLOAD_SIZE];
@@ -1068,7 +1117,7 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
log_warn(LD_PROTOCOL,
"Got an INTRODUCE2 over a non-introduction circuit %d.",
circuit->_base.n_circ_id);
- return -1;
+ goto err;
}
#ifndef NON_ANONYMOUS_MODE_ENABLED
@@ -1086,7 +1135,7 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
DH_KEY_LEN+42) {
log_warn(LD_PROTOCOL, "Got a truncated INTRODUCE2 cell on circ %d.",
circuit->_base.n_circ_id);
- return -1;
+ goto err;
}
/* look up service depending on circuit. */
@@ -1096,7 +1145,7 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
log_warn(LD_BUG, "Internal error: Got an INTRODUCE2 cell on an intro "
"circ for an unrecognized service %s.",
escaped(serviceid));
- return -1;
+ goto err;
}
/* use intro key instead of service key. */
@@ -1109,14 +1158,14 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
(char*)request, REND_SERVICE_ID_LEN);
log_warn(LD_REND, "Got an INTRODUCE2 cell for the wrong service (%s).",
escaped(serviceid));
- return -1;
+ goto err;
}
keylen = crypto_pk_keysize(intro_key);
if (request_len < keylen+DIGEST_LEN) {
log_warn(LD_PROTOCOL,
"PK-encrypted portion of INTRODUCE2 cell was truncated.");
- return -1;
+ goto err;
}
intro_point = find_intro_point(circuit);
@@ -1124,7 +1173,7 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
log_warn(LD_BUG, "Internal error: Got an INTRODUCE2 cell on an intro circ "
"(for service %s) with no corresponding rend_intro_point_t.",
escaped(serviceid));
- return -1;
+ goto err;
}
if (!service->accepted_intro_dh_parts)
@@ -1143,7 +1192,7 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
log_warn(LD_REND, "Possible replay detected! We received an "
"INTRODUCE2 cell with same PK-encrypted part %d seconds ago. "
"Dropping cell.", (int)(now-*access_time));
- return -1;
+ goto err;
}
access_time = tor_malloc(sizeof(time_t));
*access_time = now;
@@ -1159,7 +1208,7 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
PK_PKCS1_OAEP_PADDING,1);
if (r<0) {
log_warn(LD_PROTOCOL, "Couldn't decrypt INTRODUCE2 cell.");
- return -1;
+ goto err;
}
len = r;
if (*buf == 3) {
@@ -1174,7 +1223,7 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
if (auth_len != REND_DESC_COOKIE_LEN) {
log_info(LD_REND, "Wrong auth data size %d, should be %d.",
(int)auth_len, REND_DESC_COOKIE_LEN);
- return -1;
+ goto err;
}
memcpy(auth_data, buf+4, sizeof(auth_data));
v3_shift += 2+REND_DESC_COOKIE_LEN;
@@ -1235,12 +1284,12 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
if (!ptr || ptr == rp_nickname) {
log_warn(LD_PROTOCOL,
"Couldn't find a nul-padded nickname in INTRODUCE2 cell.");
- return -1;
+ goto err;
}
if ((version == 0 && !is_legal_nickname(rp_nickname)) ||
(version == 1 && !is_legal_nickname_or_hexdigest(rp_nickname))) {
log_warn(LD_PROTOCOL, "Bad nickname in INTRODUCE2 cell.");
- return -1;
+ goto err;
}
/* Okay, now we know that a nickname is at the start of the buffer. */
ptr = rp_nickname+nickname_field_len;
@@ -1404,15 +1453,24 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
memcpy(cpath->handshake_digest, keys, DIGEST_LEN);
if (extend_info) extend_info_free(extend_info);
- memset(keys, 0, sizeof(keys));
- return 0;
+ goto done;
+
err:
- memset(keys, 0, sizeof(keys));
+ status = -1;
if (dh) crypto_dh_free(dh);
if (launched)
circuit_mark_for_close(TO_CIRCUIT(launched), reason);
if (extend_info) extend_info_free(extend_info);
- return -1;
+ done:
+ memset(keys, 0, sizeof(keys));
+ memset(buf, 0, sizeof(buf));
+ memset(serviceid, 0, sizeof(serviceid));
+ memset(hexcookie, 0, sizeof(hexcookie));
+ memset(intro_key_digest, 0, sizeof(intro_key_digest));
+ memset(auth_data, 0, sizeof(auth_data));
+ memset(diffie_hellman_hash, 0, sizeof(diffie_hellman_hash));
+
+ return status;
}
/** Called when we fail building a rendezvous circuit at some point other
@@ -1600,8 +1658,8 @@ rend_service_intro_has_opened(origin_circuit_t *circuit)
this case, we might as well close the thing. */
log_info(LD_CIRC|LD_REND, "We have just finished an introduction "
"circuit, but we already have enough. Closing it.");
- circuit_mark_for_close(TO_CIRCUIT(circuit), END_CIRC_REASON_NONE);
- return;
+ reason = END_CIRC_REASON_NONE;
+ goto err;
} else {
tor_assert(circuit->build_state->is_internal);
log_info(LD_CIRC|LD_REND, "We have just finished an introduction "
@@ -1622,7 +1680,7 @@ rend_service_intro_has_opened(origin_circuit_t *circuit)
}
circuit_has_opened(circuit);
- return;
+ goto done;
}
}
@@ -1668,9 +1726,16 @@ rend_service_intro_has_opened(origin_circuit_t *circuit)
goto err;
}
- return;
+ goto done;
+
err:
circuit_mark_for_close(TO_CIRCUIT(circuit), reason);
+ done:
+ memset(buf, 0, sizeof(buf));
+ memset(auth, 0, sizeof(auth));
+ memset(serviceid, 0, sizeof(serviceid));
+
+ return;
}
/** Called when we get an INTRO_ESTABLISHED cell; mark the circuit as a
@@ -1813,9 +1878,16 @@ rend_service_rendezvous_has_opened(origin_circuit_t *circuit)
/* Change the circuit purpose. */
circuit_change_purpose(TO_CIRCUIT(circuit), CIRCUIT_PURPOSE_S_REND_JOINED);
- return;
+ goto done;
+
err:
circuit_mark_for_close(TO_CIRCUIT(circuit), reason);
+ done:
+ memset(buf, 0, sizeof(buf));
+ memset(serviceid, 0, sizeof(serviceid));
+ memset(hexcookie, 0, sizeof(hexcookie));
+
+ return;
}
/*
diff --git a/src/or/rendservice.h b/src/or/rendservice.h
index e5848785a8..baf8d5fb43 100644
--- a/src/or/rendservice.h
+++ b/src/or/rendservice.h
@@ -14,7 +14,7 @@
int num_rend_services(void);
int rend_config_services(const or_options_t *options, int validate_only);
-int rend_service_load_keys(void);
+int rend_service_load_all_keys(void);
void rend_services_introduce(void);
void rend_consider_services_upload(time_t now);
void rend_hsdir_routers_changed(void);
diff --git a/src/or/rephist.c b/src/or/rephist.c
index 720d14cf45..fa02f981f3 100644
--- a/src/or/rephist.c
+++ b/src/or/rephist.c
@@ -1136,7 +1136,7 @@ rep_hist_load_mtbf_data(time_t now)
wfu_timebuf[0] = '\0';
if (format == 1) {
- n = sscanf(line, "%40s %ld %lf S=%10s %8s",
+ n = tor_sscanf(line, "%40s %ld %lf S=%10s %8s",
hexbuf, &wrl, &trw, mtbf_timebuf, mtbf_timebuf+11);
if (n != 3 && n != 5) {
log_warn(LD_HIST, "Couldn't scan line %s", escaped(line));
@@ -1153,7 +1153,7 @@ rep_hist_load_mtbf_data(time_t now)
wfu_idx = find_next_with(lines, i+1, "+WFU ");
if (mtbf_idx >= 0) {
const char *mtbfline = smartlist_get(lines, mtbf_idx);
- n = sscanf(mtbfline, "+MTBF %lu %lf S=%10s %8s",
+ n = tor_sscanf(mtbfline, "+MTBF %lu %lf S=%10s %8s",
&wrl, &trw, mtbf_timebuf, mtbf_timebuf+11);
if (n == 2 || n == 4) {
have_mtbf = 1;
@@ -1164,7 +1164,7 @@ rep_hist_load_mtbf_data(time_t now)
}
if (wfu_idx >= 0) {
const char *wfuline = smartlist_get(lines, wfu_idx);
- n = sscanf(wfuline, "+WFU %lu %lu S=%10s %8s",
+ n = tor_sscanf(wfuline, "+WFU %lu %lu S=%10s %8s",
&wt_uptime, &total_wt_time,
wfu_timebuf, wfu_timebuf+11);
if (n == 2 || n == 4) {
diff --git a/src/test/test_util.c b/src/test/test_util.c
index 632ef68bd6..b07fa3b699 100644
--- a/src/test/test_util.c
+++ b/src/test/test_util.c
@@ -1461,12 +1461,28 @@ test_util_control_formats(void)
tor_free(out);
}
+#define test_feq(value1,value2) do { \
+ double v1 = (value1), v2=(value2); \
+ double tf_diff = v1-v2; \
+ double tf_tolerance = ((v1+v2)/2.0)/1e8; \
+ if (tf_diff<0) tf_diff=-tf_diff; \
+ if (tf_tolerance<0) tf_tolerance=-tf_tolerance; \
+ if (tf_diff<tf_tolerance) { \
+ TT_BLATHER(("%s ~~ %s: %f ~~ %f",#value1,#value2,v1,v2)); \
+ } else { \
+ TT_FAIL(("%s ~~ %s: %f != %f",#value1,#value2,v1,v2)); \
+ } \
+ } while(0)
+
static void
test_util_sscanf(void)
{
unsigned u1, u2, u3;
char s1[20], s2[10], s3[10], ch;
int r;
+ long lng1,lng2;
+ int int1, int2;
+ double d1,d2,d3,d4;
/* Simple tests (malformed patterns, literal matching, ...) */
test_eq(-1, tor_sscanf("123", "%i", &r)); /* %i is not supported */
@@ -1595,6 +1611,65 @@ test_util_sscanf(void)
test_eq(4, tor_sscanf("1.2.3 foobar", "%u.%u.%u%c", &u1, &u2, &u3, &ch));
test_eq(' ', ch);
+ r = tor_sscanf("12345 -67890 -1", "%d %ld %d", &int1, &lng1, &int2);
+ test_eq(r,3);
+ test_eq(int1, 12345);
+ test_eq(lng1, -67890);
+ test_eq(int2, -1);
+
+#if SIZEOF_INT == 4
+ r = tor_sscanf("-2147483648. 2147483647.", "%d. %d.", &int1, &int2);
+ test_eq(r,2);
+ test_eq(int1, -2147483648);
+ test_eq(int2, 2147483647);
+
+ r = tor_sscanf("-2147483679.", "%d.", &int1);
+ test_eq(r,0);
+
+ r = tor_sscanf("2147483678.", "%d.", &int1);
+ test_eq(r,0);
+#elif SIZEOF_INT == 8
+ r = tor_sscanf("-9223372036854775808. 9223372036854775807.",
+ "%d. %d.", &int1, &int2);
+ test_eq(r,2);
+ test_eq(int1, -9223372036854775808);
+ test_eq(int2, 9223372036854775807);
+
+ r = tor_sscanf("-9223372036854775809.", "%d.", &int1);
+ test_eq(r,0);
+
+ r = tor_sscanf("9223372036854775808.", "%d.", &int1);
+ test_eq(r,0);
+#endif
+
+#if SIZEOF_LONG == 4
+ r = tor_sscanf("-2147483648. 2147483647.", "%ld. %ld.", &lng1, &lng2)
+ test_eq(r,2);
+ test_eq(lng1, -2147483647 - 1);
+ test_eq(lng2, 2147483647);
+#elif SIZEOF_LONG == 8
+ r = tor_sscanf("-9223372036854775808. 9223372036854775807.",
+ "%ld. %ld.", &lng1, &lng2);
+ test_eq(r,2);
+ test_eq(lng1, -9223372036854775807L - 1);
+ test_eq(lng2, 9223372036854775807L);
+
+ r = tor_sscanf("-9223372036854775808. 9223372036854775808.",
+ "%ld. %ld.", &lng1, &lng2);
+ test_eq(r,1);
+ r = tor_sscanf("-9223372036854775809. 9223372036854775808.",
+ "%ld. %ld.", &lng1, &lng2);
+ test_eq(r,0);
+#endif
+
+ r = tor_sscanf("123.456 .000007 -900123123.2000787 00003.2",
+ "%lf %lf %lf %lf", &d1,&d2,&d3,&d4);
+ test_eq(r,4);
+ test_feq(d1, 123.456);
+ test_feq(d2, .000007);
+ test_feq(d3, -900123123.2000787);
+ test_feq(d4, 3.2);
+
done:
;
}
diff --git a/src/win32/orconfig.h b/src/win32/orconfig.h
index 3449277d42..4c8c65518a 100644
--- a/src/win32/orconfig.h
+++ b/src/win32/orconfig.h
@@ -232,7 +232,7 @@
#define USING_TWOS_COMPLEMENT
/* Version number of package */
-#define VERSION "0.2.3.17-beta-dev"
+#define VERSION "0.2.4.0-alpha-dev"