The unified diff between revisions [869f0c37..] and [e24ccc23..] is displayed below. It can also be downloaded as a raw diff.
#
#
# add_file "tests/t_lua_includedir.at"
# content [5b4ce1e6a2781a7a841039a1e02a96cc0221f3ff]
#
# add_file "tests/t_rcfile_dir.at"
# content [06128ab21f137c3a69e4fce2db25b0ea3fa424de]
#
# patch "ChangeLog"
# from [3d5fd1c1a0ef8a2d86cbd76a30d89c18f15fd496]
# to [1a92ac866998074c2b9d6e2795fe322656a4666b]
#
# patch "INSTALL"
# from [91300af1b7411653bb3eff5608a548b6a5ed633b]
# to [400eaf8e798d44fce8b0622c88bc324fd31830c2]
#
# patch "Makefile.am"
# from [bedd826d1991853466ab57d77d80be1635014d17]
# to [8f57b338067b43d1d1ae5de118323a6c67f6be7d]
#
# patch "app_state.cc"
# from [7059471c5febbb78676ba5b2e7e432ce3fe39977]
# to [1e2cfd4af8c4ccc2b721c758469659dc3d7f4131]
#
# patch "commands.cc"
# from [0219fa13f31351d209832ea210bb1e7a46bef761]
# to [2b9bb2299e2fe0f337e33d1989d6f3f6d8c793c7]
#
# patch "constants.cc"
# from [eabc0d5838a67e3e4d351ce3d28eac0387f773ae]
# to [2258f6165b6db8f8c952f97aaacf0addbbed060f]
#
# patch "constants.hh"
# from [4a4494bcaa97ec07c147fc066564519bb4d13481]
# to [a4741c95ae4faeb5db1f36f6ccf406f732250241]
#
# patch "database.cc"
# from [181e7b79e4435b2ffa1561ce8b9162a41563ed72]
# to [1ce26993177acbeb3e3d18aba17f43700372fa3c]
#
# patch "diff_patch.cc"
# from [cb8bea5dd5b9e5b9c4eb838dd89bb85200c434aa]
# to [fbef84d6f575c4f14850b8a8e7d78e42021a0e80]
#
# patch "keys.cc"
# from [a4ccef56d24ee9cda7a009144f5476e3cdcb07d7]
# to [9c50dc0d669ffb6ad2328c46485bf3ae65dad4b5]
#
# patch "keys.hh"
# from [f10aaea8dbaf4005a55ec388f278d1bb81d664af]
# to [ec2da9cbde80341456204b6e0e0b4f727388ed8b]
#
# patch "lua.cc"
# from [c5a01c71aef633d6ad5ad78650e4e0601295b349]
# to [e4dd123e0c07b59361555b521f3b2cc17aa445b5]
#
# patch "monotone.texi"
# from [28008d0dc7e6abd7e2e40cab143add9c4faecbca]
# to [131933a2c2cd95c13bf8e3b8c988b8e4bc97664d]
#
# patch "netcmd.cc"
# from [3d925448e00e4f5a0e70f9ac977899fbf9312a1e]
# to [207caa3c268ad7e6a93eaff3f3298aa53f45fadd]
#
# patch "netcmd.hh"
# from [901532e162e97d3df8f89ccc1198203eef8ed8fe]
# to [b010800c5f7c9cb23ac8f0ac069b7a4fbdd17774]
#
# patch "netsync.cc"
# from [218fabbe4a06f50089a50833c72f9c1b4ed78a91]
# to [cba71cd069c2658e79568849fe0328c92695547d]
#
# patch "std_hooks.lua"
# from [c06a28443baa42ca4d4f349a6dd9653d25fa8c0f]
# to [9b1a27e30129929aa366223e51f1c11ec918fc93]
#
# patch "tests/t_cat_file_by_name.at"
# from [009033538c4424ad6994c33eef9dab24bef15980]
# to [45f9704b56b2b74f16c5579539bc1c6f777b1e56]
#
# patch "tests/t_netsync_diffbranch.at"
# from [a529b5b568039685d66db00c8550b817fbc3a4a4]
# to [6a2a4e9b6dada24dea2ad88bd0c3d0caf8d180bc]
#
# patch "testsuite.at"
# from [2635480ab5d48f5eae96909a5e59ef20a7b0c36d]
# to [2e17e62213b9878d08d7c792f244b6e193498618]
#
# patch "vocab.cc"
# from [6e07baffa263c80f013d474ad362c3b52c162201]
# to [0a568b1d7a988e146671055888136fdff7818916]
#
# patch "vocab_terms.hh"
# from [c12dd87ed775d3e2d8713a393cc055ce68e5ae16]
# to [fe3bf7a7700bdde82211ba0be39caf248025e00b]
#
============================================================
--- tests/t_lua_includedir.at 5b4ce1e6a2781a7a841039a1e02a96cc0221f3ff
+++ tests/t_lua_includedir.at 5b4ce1e6a2781a7a841039a1e02a96cc0221f3ff
@@ -0,0 +1,44 @@
+AT_SETUP([include() and includedir() lua functions])
+MONOTONE_SETUP
+
+AT_CHECK(mkdir gongolo)
+
+AT_DATA(include.lua, [include("../gongolo/aaa.rc")
+])
+
+AT_DATA(includedir.lua, [includedir("../gongolo")
+])
+
+# write two files and check that they will be invoked in alphabetic order
+AT_DATA(gongolo/aaa.rc, [function paraponzi()
+ io.write("BOOGA BOOGA")
+end
+paraponzi()
+])
+AT_DATA(gongolo/bbb.zz, [function labellagigogin()
+ io.write("CICCA CICCA")
+end
+labellagigogin()
+])
+
+# setup a wrk dir
+AT_CHECK(MONOTONE setup alt_wrk, [], [ignore], [ignore])
+
+# include directly a single file
+AT_CHECK(cd alt_wrk && MONOTONE --root=. --rcfile=../include.lua status, [], [stdout], [ignore])
+AT_CHECK(grep -q "BOOGA BOOGA" stdout)
+
+# include dirs
+AT_CHECK(cd alt_wrk && MONOTONE --root=. --rcfile=../includedir.lua status, [], [stdout], [ignore])
+AT_CHECK(grep -q "BOOGA BOOGACICCA CICCA" stdout)
+
+# write a third file: should be read beetween the two previous ones
+AT_DATA(gongolo/aba.rc, [function notwowithoutthree()
+ io.write("hu hu")
+end
+notwowithoutthree()
+])
+AT_CHECK(cd alt_wrk && MONOTONE --root=. --rcfile=../includedir.lua status, [], [stdout], [ignore])
+AT_CHECK(grep -q "BOOGA BOOGAhu huCICCA CICCA" stdout)
+
+AT_CLEANUP
============================================================
--- tests/t_rcfile_dir.at 06128ab21f137c3a69e4fce2db25b0ea3fa424de
+++ tests/t_rcfile_dir.at 06128ab21f137c3a69e4fce2db25b0ea3fa424de
@@ -0,0 +1,32 @@
+AT_SETUP([--rcfile=directory])
+MONOTONE_SETUP
+
+AT_CHECK(mkdir gongolo)
+
+# write two files and check that they will be invoked in alphabetic order
+AT_DATA(gongolo/aaa.rc, [function paraponzi()
+ io.write("BOOGA BOOGA")
+end
+paraponzi()
+])
+AT_DATA(gongolo/bbb.rc, [function labellagigogin()
+ io.write("CICCA CICCA")
+end
+labellagigogin()
+])
+
+# note: rcfile is placed outside workdir
+AT_CHECK(MONOTONE setup alt_wrk, [], [ignore], [ignore])
+AT_CHECK(cd alt_wrk && MONOTONE --root=. --rcfile=../gongolo status, [], [stdout], [ignore])
+AT_CHECK(grep -q "BOOGA BOOGACICCA CICCA" stdout)
+
+# write a third file: should be read beetween the two previous ones
+AT_DATA(gongolo/aba.rc, [function notwowithoutthree()
+ io.write("hu hu")
+end
+notwowithoutthree()
+])
+AT_CHECK(cd alt_wrk && MONOTONE --root=. --rcfile=../gongolo status, [], [stdout], [ignore])
+AT_CHECK(grep -q "BOOGA BOOGAhu huCICCA CICCA" stdout)
+
+AT_CLEANUP
============================================================
--- ChangeLog 3d5fd1c1a0ef8a2d86cbd76a30d89c18f15fd496
+++ ChangeLog 1a92ac866998074c2b9d6e2795fe322656a4666b
@@ -1,3 +1,97 @@
+2005-06-22 Nathaniel Smith <njs@codesourcery.com>
+
+ * netcmd.hh (netcmd::read, netcmd::write): Don't have defaults for
+ key/hmac arguments.
+ * netcmd.cc (do_netcmd_roundtrip): New function.
+ (test_netcmd_functions): Use it. Also, make work with hmac
+ changes.
+ (test_netcmd_mac): New test.
+ (add_netcmd_tests): Call it.
+
+2005-06-22 Nathaniel Smith <njs@codesourcery.com>
+
+ * netcmd.cc (read): Remove unused variable.
+ * netsync.cc (call_server, process)
+ (arm_sessions_and_calculate_probe, handle_read_available): Give
+ better error message on bad_decode exceptions.
+
+2005-06-22 Nathaniel Smith <njs@codesourcery.com>
+
+ * netcmd.cc, netsync.cc: Revert backwards compatibility code; 0.19
+ and 0.20 can't be usefully compatible, and the code as it existed
+ would cause real version mismatch error reporting to not work
+ right. (Old client with new server would give a generic "server
+ disconnected" error message instead of something useful.)
+
+2005-06-21 Nathaniel Smith <njs@codesourcery.com>
+
+ * netsync.cc (rebuild_merkle_trees): Fix FIXME comments to match
+ reality.
+ * tests/t_netsync_diffbranch.at: No longer a bug, remove
+ priority.
+
+2005-06-20 Nathaniel Smith <njs@codesourcery.com>
+
+ * monotone.texi (Hook Reference): Oops, missed a @ref.
+
+2005-06-20 Nathaniel Smith <njs@codesourcery.com>
+
+ * monotone.texi (Default monotonerc): Rename section to...
+ (Default hooks): ...this, to emphasize is still read even when a
+ monotonerc exists.
+
+2005-06-19 Richard Levitte <richard@levitte.org>
+
+ * Makefile.am: There's no reason for monotone.pdf or .dvi to
+ depend on monotone.info, since they are built from the .texi
+ files. Also, make the monotone.html and html targets depend
+ on version.texi and std_hooks.lua as well.
+
+2005-06-18 Matt Johnston <matt@ucc.asn.au>
+
+ * INSTALL: fix typo, should be -Iboost_1_31_0 not -Iboost_1_31_2
+
+2005-06-18 Riccardo Ghetta <birrachiara@tin.it>
+ * monotone.texi: include std_hooks.lua as an appendix and remove long
+ lua excerpts from hook reference.
+ * Makefile.am : make monotone.pdf/eps depend on monotone.info
+
+2005-06-17 Matt Johnston <matt@ucc.asn.au>
+
+ * database.cc (database::execute()): truncate long query log messages
+ before copying, saving memory.
+ Patch from Eric Anderson < ea at cello hpl hp com >
+
+2005-06-17 Riccardo Ghetta <birrachiara@tin.it>
+ Adds include()/includedir() to lua hooks and extend --rcfile
+ * lua.cc: handle --rcfile with directories, implement
+ include() and includedir()
+ * testsuite.at, t_lua_includedir.at, t_rcfile_dir.at:
+ test new functionality
+ * monotone.texi: document all functions available to hook
+ writers, including the new include() and includedir()
+
+2005-06-16 Nathaniel Smith <njs@codesourcery.com>
+
+ * diff_patch.cc (merge_extents): Typo caught by anonymous reader.
+
+2005-06-16 Nathaniel Smith <njs@codesourcery.com>
+
+ * commands.cc (cat): Account for being in a subdir in 'cat file
+ REV PATH'.
+ * tests/t_cat_file_by_name.at: Test.
+
+2005-06-17 Richard Levitte <richard@levitte.org>
+
+ * app_state.cc (app_state::app_state()): Avoid a gcc warning by
+ having the class members initialised in the same order they are
+ defined in the class.
+
+2005-06-16 Nathaniel Smith <njs@pobox.com>
+
+ * std_hooks.lua (ignore_file): Add Cons/SCons cache files to
+ default ignore list.
+
2005-06-16 Matt Johnston <matt@ucc.asn.au>
* ui.cc: increase the divisor as required so that we don't get spurious
@@ -5,6 +99,11 @@ 2005-06-16 Matt Johnston <matt@ucc.asn
2005-06-16 Matt Johnston <matt@ucc.asn.au>
+ * ui.cc: increase the divisor as required so that we don't get spurious
+ screen updates when we're using the kilobyte/megabyte tickers
+
+2005-06-16 Matt Johnston <matt@ucc.asn.au>
+
* netcmd.{cc,hh}, netsync.cc: only read a single command packet
worth of data at a time, to avoid having to shuffle buffers about.
============================================================
--- INSTALL 91300af1b7411653bb3eff5608a548b6a5ed633b
+++ INSTALL 400eaf8e798d44fce8b0622c88bc324fd31830c2
@@ -69,7 +69,7 @@ 1.1 building boost:
standard include path, or you can pass additional configuration
options to your monotone configure build, such as:
- ./configure CPPFLAGS="-Iboost_1_31_2" LDFLAGS="-Lboost_1_31_0/libs"
+ ./configure CPPFLAGS="-Iboost_1_31_0" LDFLAGS="-Lboost_1_31_0/libs"
monotone does not use all of boost -- for instance, people often
have trouble building boost.python, which we do not use. you don't
============================================================
--- Makefile.am bedd826d1991853466ab57d77d80be1635014d17
+++ Makefile.am 8f57b338067b43d1d1ae5de118323a6c67f6be7d
@@ -297,10 +297,12 @@ MAKEINFOFLAGS=-I $(top_builddir)
MAKEINFOFLAGS=-I $(top_builddir)
-monotone.pdf: monotone.texi version.texi $(PDF_FIGURES)
+monotone.info: monotone.texi version.texi std_hooks.lua
-monotone.dvi: monotone.texi version.texi $(EPS_FIGURES)
+monotone.pdf: monotone.texi version.texi std_hooks.lua $(PDF_FIGURES)
+monotone.dvi: monotone.texi version.texi std_hooks.lua $(EPS_FIGURES)
+
#%.eps: %.epsi
# mv $< $@
#
@@ -408,14 +410,14 @@ package_full_revision.c: package_full_re
# automake doesn't build html docs
-monotone.html: monotone.texi texinfo.css
+monotone.html: monotone.texi version.texi std_hooks.lua texinfo.css
makeinfo --no-split --no-headers --output $@ --html $<
perl -i.perlbak -pe 's@<head>@<head>\n<link type="text/css" rel="stylesheet" href="texinfo.css" />@' $@
rm -f monotone.html.perlbak
# The .perlbak thing is needed, otherwise the perl executions fails on MinGW
-html: monotone.texi texinfo.css
+html: monotone.texi version.texi std_hooks.lua texinfo.css
mkdir -p html
makeinfo --number-sections --html --output html $<
perl -i.perlbak -pe 's@<head>@<head>\n<link type="text/css" rel="stylesheet" href="texinfo.css" />@' html/*.html
============================================================
--- app_state.cc 7059471c5febbb78676ba5b2e7e432ce3fe39977
+++ app_state.cc 1e2cfd4af8c4ccc2b721c758469659dc3d7f4131
@@ -31,7 +31,7 @@ app_state::app_state()
app_state::app_state()
: branch_name(""), db(""), stdhooks(true), rcfiles(true), diffs(false),
- search_root("/"), depth(-1), last(-1), verbose(false)
+ verbose(false), search_root("/"), depth(-1), last(-1)
{
db.set_app(this);
}
============================================================
--- commands.cc 0219fa13f31351d209832ea210bb1e7a46bef761
+++ commands.cc 2b9bb2299e2fe0f337e33d1989d6f3f6d8c793c7
@@ -1370,7 +1370,7 @@ CMD(cat, "informative",
{
revision_id rid;
complete(app, idx(args, 1)(), rid);
- file_path fp(idx(args, 2)());
+ file_path fp = app.prefix(idx(args, 2));
manifest_id mid;
app.db.get_revision_manifest(rid, mid);
manifest_map m;
============================================================
--- constants.cc eabc0d5838a67e3e4d351ce3d28eac0387f773ae
+++ constants.cc 2258f6165b6db8f8c952f97aaacf0addbbed060f
@@ -160,5 +160,9 @@ namespace constants
size_t const netsync_default_port = 5253;
size_t const netsync_connection_limit = 1024;
size_t const netsync_timeout_seconds = 21600; // 6 hours
+ size_t const netsync_session_key_length_in_bytes = 20; // 160 bits
+ size_t const netsync_hmac_value_length_in_bytes = 20; // 160 bits
+ std::string const & netsync_key_initializer = std::string(netsync_session_key_length_in_bytes, 0);
+
}
============================================================
--- constants.hh 4a4494bcaa97ec07c147fc066564519bb4d13481
+++ constants.hh a4741c95ae4faeb5db1f36f6ccf406f732250241
@@ -7,6 +7,7 @@
// see the file COPYING for details
#include <unistd.h>
+#include <string>
#include "numeric_vocab.hh"
namespace constants
@@ -120,6 +121,15 @@ namespace constants
// number of seconds a connection can be idle before it's dropped
extern size_t const netsync_timeout_seconds;
+ // netsync HMAC key length
+ extern size_t const netsync_session_key_length_in_bytes;
+
+ // netsync HMAC value length
+ extern size_t const netsync_hmac_value_length_in_bytes;
+
+ // netsync session key and HMAC key default initializer
+ extern std::string const & netsync_key_initializer;
+
}
#endif // __CONSTANTS_HH__
============================================================
--- database.cc 181e7b79e4435b2ffa1561ce8b9162a41563ed72
+++ database.cc 1ce26993177acbeb3e3d18aba17f43700372fa3c
@@ -12,6 +12,7 @@
#include <vector>
#include <stdarg.h>
+#include <string.h>
#include <boost/shared_ptr.hpp>
#include <boost/lexical_cast.hpp>
@@ -643,24 +644,28 @@ database::execute(char const * query, ..
database::execute(char const * query, ...)
{
va_list ap;
- int res;
- char * errmsg = NULL;
va_start(ap, query);
// log it
char * formatted = sqlite3_vmprintf(query, ap);
- string qq(formatted);
- if (qq.size() > constants::db_log_line_sz)
- qq = qq.substr(0, constants::db_log_line_sz) + string(" ...");
+ string qq;
+
+ if (strlen(formatted) > constants::db_log_line_sz)
+ {
+ qq.assign(formatted, constants::db_log_line_sz);
+ qq.append(" ...");
+ }
+ else
+ {
+ qq = formatted;
+ }
L(F("db.execute(\"%s\")\n") % qq);
- sqlite3_free(formatted);
- va_end(ap);
- va_start(ap, query);
-
// do it
- res = sqlite3_exec_vprintf(sql(), query, NULL, NULL, &errmsg, ap);
+ char * errmsg = NULL;
+ int res = sqlite3_exec(sql(), formatted, NULL, NULL, &errmsg);
+ sqlite3_free(formatted);
va_end(ap);
============================================================
--- diff_patch.cc cb8bea5dd5b9e5b9c4eb838dd89bb85200c434aa
+++ diff_patch.cc fbef84d6f575c4f14850b8a8e7d78e42021a0e80
@@ -307,7 +307,7 @@ void merge_extents(vector<extent> const
}
// mutual or single-edge deletes
- else if ((i->type == deleted && j->len == deleted)
+ else if ((i->type == deleted && j->type == deleted)
|| (i->type == deleted && j->type == preserved)
|| (i->type == preserved && j->type == deleted))
{
============================================================
--- keys.cc a4ccef56d24ee9cda7a009144f5476e3cdcb07d7
+++ keys.cc 9c50dc0d669ffb6ad2328c46485bf3ae65dad4b5
@@ -166,7 +166,21 @@ write_der(T & val, SecByteBlock & sec)
der_encoded[i] = '\0';
}
+static bool
+blocking_rng(lua_hooks & lua)
+{
+ if (!lua.hook_non_blocking_rng_ok())
+ {
+#ifndef BLOCKING_RNG_AVAILABLE
+ throw oops("no blocking RNG available and non-blocking RNG rejected");
+#else
+ return true;
+#endif
+ };
+ return false;
+}
+
void
generate_key_pair(lua_hooks & lua, // to hook for phrase
rsa_keypair_id const & id, // to prompting user for phrase
@@ -176,17 +190,7 @@ generate_key_pair(lua_hooks & lua,
{
// we will panic here if the user doesn't like urandom and we can't give
// them a real entropy-driven random.
- bool request_blocking_rng = false;
- if (!lua.hook_non_blocking_rng_ok())
- {
-#ifndef BLOCKING_RNG_AVAILABLE
- throw oops("no blocking RNG available and non-blocking RNG rejected");
-#else
- request_blocking_rng = true;
-#endif
- }
-
- AutoSeededRandomPool rng(request_blocking_rng);
+ AutoSeededRandomPool rng(blocking_rng(lua));
SecByteBlock phrase, pubkey, privkey;
rsa_pub_key raw_pub_key;
arc4<rsa_priv_key> raw_priv_key;
@@ -267,16 +271,7 @@ make_signature(lua_hooks & lua,
// we will panic here if the user doesn't like urandom and we can't give
// them a real entropy-driven random.
- bool request_blocking_rng = false;
- if (!lua.hook_non_blocking_rng_ok())
- {
-#ifndef BLOCKING_RNG_AVAILABLE
- throw oops("no blocking RNG available and non-blocking RNG rejected");
-#else
- request_blocking_rng = true;
-#endif
- }
- AutoSeededRandomPool rng(request_blocking_rng);
+ AutoSeededRandomPool rng(blocking_rng(lua));
// we permit the user to relax security here, by caching a decrypted key
// (if they permit it) through the life of a program run. this helps when
@@ -396,6 +391,75 @@ check_signature(lua_hooks & lua,
return vf->GetLastResult();
}
+void encrypt_rsa(lua_hooks & lua,
+ rsa_keypair_id const & id,
+ base64<rsa_pub_key> & pub_encoded,
+ std::string const & plaintext,
+ rsa_oaep_sha_data & ciphertext)
+{
+ AutoSeededRandomPool rng(blocking_rng(lua));
+
+ rsa_pub_key pub;
+ decode_base64(pub_encoded, pub);
+ SecByteBlock pub_block;
+ pub_block.Assign(reinterpret_cast<byte const *>(pub().data()), pub().size());
+ StringSource keysource(pub_block.data(), pub_block.size(), true);
+
+ shared_ptr<RSAES_OAEP_SHA_Encryptor> encryptor;
+ encryptor = shared_ptr<RSAES_OAEP_SHA_Encryptor>
+ (new RSAES_OAEP_SHA_Encryptor(keysource));
+
+ string ciphertext_string;
+ StringSource tmp(plaintext, true,
+ encryptor->CreateEncryptionFilter
+ (rng, new StringSink(ciphertext_string)));
+
+ ciphertext = rsa_oaep_sha_data(ciphertext_string);
+}
+
+void decrypt_rsa(lua_hooks & lua,
+ rsa_keypair_id const & id,
+ base64< arc4<rsa_priv_key> > const & priv,
+ rsa_oaep_sha_data const & ciphertext,
+ std::string & plaintext)
+{
+ AutoSeededRandomPool rng(blocking_rng(lua));
+ arc4<rsa_priv_key> decoded_key;
+ SecByteBlock decrypted_key;
+ SecByteBlock phrase;
+ shared_ptr<RSAES_OAEP_SHA_Decryptor> decryptor;
+
+ for (int i = 0; i < 3; i++)
+ {
+ bool force = false;
+ decode_base64(priv, decoded_key);
+ decrypted_key.Assign(reinterpret_cast<byte const *>(decoded_key().data()),
+ decoded_key().size());
+ get_passphrase(lua, id, phrase, false, force);
+
+ try
+ {
+ do_arc4(phrase, decrypted_key);
+ StringSource keysource(decrypted_key.data(), decrypted_key.size(), true);
+ decryptor = shared_ptr<RSAES_OAEP_SHA_Decryptor>
+ (new RSAES_OAEP_SHA_Decryptor(keysource));
+ }
+ catch (...)
+ {
+ if (i >= 2)
+ throw informative_failure("failed to decrypt private RSA key, "
+ "probably incorrect passphrase");
+ // don't use the cache bad one next time
+ force = true;
+ continue;
+ }
+ }
+
+ StringSource tmp(ciphertext(), true,
+ decryptor->CreateDecryptionFilter
+ (rng, new StringSink(plaintext)));
+}
+
void
read_pubkey(string const & in,
rsa_keypair_id & id,
============================================================
--- keys.hh f10aaea8dbaf4005a55ec388f278d1bb81d664af
+++ keys.hh ec2da9cbde80341456204b6e0e0b4f727388ed8b
@@ -42,6 +42,18 @@ void require_password(rsa_keypair_id con
void require_password(rsa_keypair_id const & id,
app_state & app);
+void encrypt_rsa(lua_hooks & lua,
+ rsa_keypair_id const & id,
+ base64<rsa_pub_key> & pub,
+ std::string const & plaintext,
+ rsa_oaep_sha_data & ciphertext);
+
+void decrypt_rsa(lua_hooks & lua,
+ rsa_keypair_id const & id,
+ base64< arc4<rsa_priv_key> > const & priv,
+ rsa_oaep_sha_data const & ciphertext,
+ std::string & plaintext);
+
// netsync stuff
void read_pubkey(std::string const & in,
============================================================
--- lua.cc c5a01c71aef633d6ad5ad78650e4e0601295b349
+++ lua.cc e4dd123e0c07b59361555b521f3b2cc17aa445b5
@@ -30,6 +30,7 @@ extern "C" {
#include "sanity.hh"
#include "vocab.hh"
#include "platform.hh"
+#include "transforms.hh"
// defined in {std,test}_hooks.lua, converted
#include "test_hooks.h"
@@ -45,164 +46,6 @@ static int panic_thrower(lua_State * st)
}
*/
-extern "C"
-{
- static int
- monotone_mkstemp_for_lua(lua_State *L)
- {
- int fd = -1;
- FILE **pf = NULL;
- char const *filename = lua_tostring (L, -1);
- std::string dup(filename);
-
- fd = monotone_mkstemp(dup);
-
- if (fd == -1)
- return 0;
-
- // this magic constructs a lua object which the lua io library
- // will enjoy working with
- pf = static_cast<FILE **>(lua_newuserdata(L, sizeof(FILE *)));
- *pf = fdopen(fd, "r+");
- lua_pushstring(L, "FILE*");
- lua_rawget(L, LUA_REGISTRYINDEX);
- lua_setmetatable(L, -2);
-
- lua_pushstring(L, dup.c_str());
-
- if (*pf == NULL)
- {
- lua_pushnil(L);
- lua_pushfstring(L, "%s", strerror(errno));
- lua_pushnumber(L, errno);
- return 3;
- }
- else
- return 2;
- }
-
- static int
- monotone_existsonpath_for_lua(lua_State *L)
- {
- const char *exe = lua_tostring(L, -1);
- lua_pushnumber(L, existsonpath(exe));
- return 1;
- }
-
- static int
- monotone_is_executable_for_lua(lua_State *L)
- {
- const char *path = lua_tostring(L, -1);
- lua_pushboolean(L, is_executable(path));
- return 1;
- }
-
- static int
- monotone_make_executable_for_lua(lua_State *L)
- {
- const char *path = lua_tostring(L, -1);
- lua_pushnumber(L, make_executable(path));
- return 1;
- }
-
- static int
- monotone_spawn_for_lua(lua_State *L)
- {
- int n = lua_gettop(L);
- const char *path = lua_tostring(L, -n);
- char **argv = (char**)malloc((n+1)*sizeof(char*));
- int i;
- pid_t ret;
- if (argv==NULL)
- return 0;
- argv[0] = (char*)path;
- for (i=1; i<n; i++) argv[i] = (char*)lua_tostring(L, -(n - i));
- argv[i] = NULL;
- ret = process_spawn(argv);
- free(argv);
- lua_pushnumber(L, ret);
- return 1;
- }
-
- static int
- monotone_wait_for_lua(lua_State *L)
- {
- pid_t pid = (pid_t)lua_tonumber(L, -1);
- int res;
- int ret;
- ret = process_wait(pid, &res);
- lua_pushnumber(L, res);
- lua_pushnumber(L, ret);
- return 2;
- }
-
- static int
- monotone_kill_for_lua(lua_State *L)
- {
- int n = lua_gettop(L);
- pid_t pid = (pid_t)lua_tonumber(L, -2);
- int sig;
- if (n>1)
- sig = (int)lua_tonumber(L, -1);
- else
- sig = SIGTERM;
- lua_pushnumber(L, process_kill(pid, sig));
- return 1;
- }
-
- static int
- monotone_sleep_for_lua(lua_State *L)
- {
- int seconds = (int)lua_tonumber(L, -1);
- lua_pushnumber(L, process_sleep(seconds));
- return 1;
- }
-
- static int
- monotone_guess_binary_for_lua(lua_State *L)
- {
- const char *path = lua_tostring(L, -1);
- N(path, F("guess_binary called with an invalid parameter"));
- lua_pushboolean(L, guess_binary(std::string(path, lua_strlen(L, -1))));
- return 1;
- }
-}
-
-
-lua_hooks::lua_hooks()
-{
- st = lua_open ();
- I(st);
-
- // no atpanic support in 4.x
- // lua_atpanic (st, &panic_thrower);
-
- luaopen_base(st);
- luaopen_io(st);
- luaopen_string(st);
- luaopen_math(st);
- luaopen_table(st);
- luaopen_debug(st);
-
- // add monotone-specific functions
- lua_register(st, "mkstemp", monotone_mkstemp_for_lua);
- lua_register(st, "existsonpath", monotone_existsonpath_for_lua);
- lua_register(st, "is_executable", monotone_is_executable_for_lua);
- lua_register(st, "make_executable", monotone_make_executable_for_lua);
- lua_register(st, "spawn", monotone_spawn_for_lua);
- lua_register(st, "wait", monotone_wait_for_lua);
- lua_register(st, "kill", monotone_kill_for_lua);
- lua_register(st, "sleep", monotone_sleep_for_lua);
- lua_register(st, "guess_binary", monotone_guess_binary_for_lua);
-}
-
-lua_hooks::~lua_hooks()
-{
- if (st)
- lua_close (st);
-}
-
-
// This Lua object represents a single imperative transaction with the lua
// interpreter. if it fails at any point, all further commands in the
// transaction are ignored. it cleans the lua stack up when it is
@@ -535,6 +378,217 @@ std::set<string> Lua::missing_functions;
std::set<string> Lua::missing_functions;
+
+
+
+extern "C"
+{
+ static int
+ monotone_mkstemp_for_lua(lua_State *L)
+ {
+ int fd = -1;
+ FILE **pf = NULL;
+ char const *filename = lua_tostring (L, -1);
+ std::string dup(filename);
+
+ fd = monotone_mkstemp(dup);
+
+ if (fd == -1)
+ return 0;
+
+ // this magic constructs a lua object which the lua io library
+ // will enjoy working with
+ pf = static_cast<FILE **>(lua_newuserdata(L, sizeof(FILE *)));
+ *pf = fdopen(fd, "r+");
+ lua_pushstring(L, "FILE*");
+ lua_rawget(L, LUA_REGISTRYINDEX);
+ lua_setmetatable(L, -2);
+
+ lua_pushstring(L, dup.c_str());
+
+ if (*pf == NULL)
+ {
+ lua_pushnil(L);
+ lua_pushfstring(L, "%s", strerror(errno));
+ lua_pushnumber(L, errno);
+ return 3;
+ }
+ else
+ return 2;
+ }
+
+ static int
+ monotone_existsonpath_for_lua(lua_State *L)
+ {
+ const char *exe = lua_tostring(L, -1);
+ lua_pushnumber(L, existsonpath(exe));
+ return 1;
+ }
+
+ static int
+ monotone_is_executable_for_lua(lua_State *L)
+ {
+ const char *path = lua_tostring(L, -1);
+ lua_pushboolean(L, is_executable(path));
+ return 1;
+ }
+
+ static int
+ monotone_make_executable_for_lua(lua_State *L)
+ {
+ const char *path = lua_tostring(L, -1);
+ lua_pushnumber(L, make_executable(path));
+ return 1;
+ }
+
+ static int
+ monotone_spawn_for_lua(lua_State *L)
+ {
+ int n = lua_gettop(L);
+ const char *path = lua_tostring(L, -n);
+ char **argv = (char**)malloc((n+1)*sizeof(char*));
+ int i;
+ pid_t ret;
+ if (argv==NULL)
+ return 0;
+ argv[0] = (char*)path;
+ for (i=1; i<n; i++) argv[i] = (char*)lua_tostring(L, -(n - i));
+ argv[i] = NULL;
+ ret = process_spawn(argv);
+ free(argv);
+ lua_pushnumber(L, ret);
+ return 1;
+ }
+
+ static int
+ monotone_wait_for_lua(lua_State *L)
+ {
+ pid_t pid = (pid_t)lua_tonumber(L, -1);
+ int res;
+ int ret;
+ ret = process_wait(pid, &res);
+ lua_pushnumber(L, res);
+ lua_pushnumber(L, ret);
+ return 2;
+ }
+
+ static int
+ monotone_kill_for_lua(lua_State *L)
+ {
+ int n = lua_gettop(L);
+ pid_t pid = (pid_t)lua_tonumber(L, -2);
+ int sig;
+ if (n>1)
+ sig = (int)lua_tonumber(L, -1);
+ else
+ sig = SIGTERM;
+ lua_pushnumber(L, process_kill(pid, sig));
+ return 1;
+ }
+
+ static int
+ monotone_sleep_for_lua(lua_State *L)
+ {
+ int seconds = (int)lua_tonumber(L, -1);
+ lua_pushnumber(L, process_sleep(seconds));
+ return 1;
+ }
+
+ static int
+ monotone_guess_binary_for_lua(lua_State *L)
+ {
+ const char *path = lua_tostring(L, -1);
+ N(path, F("guess_binary called with an invalid parameter"));
+ lua_pushboolean(L, guess_binary(std::string(path, lua_strlen(L, -1))));
+ return 1;
+ }
+
+ static int
+ monotone_include_for_lua(lua_State *L)
+ {
+ const char *path = lua_tostring(L, -1);
+ N(path, F("Include called with an invalid parameter"));
+
+ bool res =Lua(L)
+ .loadfile(std::string(path, lua_strlen(L, -1)))
+ .call(0,1)
+ .ok();
+
+ lua_pushboolean(L, res);
+ return 1;
+ }
+
+ static int
+ monotone_includedir_for_lua(lua_State *L)
+ {
+ const char *pathstr = lua_tostring(L, -1);
+ N(pathstr, F("IncludeDir called with an invalid parameter"));
+
+ fs::path locpath(pathstr);
+ N(fs::exists(locpath), F("Directory '%s' does not exists") % pathstr);
+ N(fs::is_directory(locpath), F("'%s' is not a directory") % pathstr);
+
+ // directory, iterate over it, skipping subdirs, taking every filename,
+ // sorting them and loading in sorted order
+ fs::directory_iterator it(locpath);
+ std::vector<fs::path> arr;
+ while (it != fs::directory_iterator())
+ {
+ if (!fs::is_directory(*it))
+ arr.push_back(*it);
+ ++it;
+ }
+ std::sort(arr.begin(), arr.end());
+ for (std::vector<fs::path>::iterator i= arr.begin(); i != arr.end(); ++i)
+ {
+ bool res =Lua(L)
+ .loadfile(i->string())
+ .call(0,1)
+ .ok();
+ N(res, F("lua error while loading rcfile '%s'") % i->string());
+ }
+
+ lua_pushboolean(L, true);
+ return 1;
+ }
+}
+
+
+lua_hooks::lua_hooks()
+{
+ st = lua_open ();
+ I(st);
+
+ // no atpanic support in 4.x
+ // lua_atpanic (st, &panic_thrower);
+
+ luaopen_base(st);
+ luaopen_io(st);
+ luaopen_string(st);
+ luaopen_math(st);
+ luaopen_table(st);
+ luaopen_debug(st);
+
+ // add monotone-specific functions
+ lua_register(st, "mkstemp", monotone_mkstemp_for_lua);
+ lua_register(st, "existsonpath", monotone_existsonpath_for_lua);
+ lua_register(st, "is_executable", monotone_is_executable_for_lua);
+ lua_register(st, "make_executable", monotone_make_executable_for_lua);
+ lua_register(st, "spawn", monotone_spawn_for_lua);
+ lua_register(st, "wait", monotone_wait_for_lua);
+ lua_register(st, "kill", monotone_kill_for_lua);
+ lua_register(st, "sleep", monotone_sleep_for_lua);
+ lua_register(st, "guess_binary", monotone_guess_binary_for_lua);
+ lua_register(st, "include", monotone_include_for_lua);
+ lua_register(st, "includedir", monotone_includedir_for_lua);
+}
+
+lua_hooks::~lua_hooks()
+{
+ if (st)
+ lua_close (st);
+}
+
static bool
run_string(lua_State * st, string const &str, string const & identity)
{
@@ -591,6 +645,29 @@ lua_hooks::load_rcfile(utf8 const & rc)
lua_hooks::load_rcfile(utf8 const & rc)
{
I(st);
+ if (rc() != "-")
+ {
+ fs::path locpath(localized(rc));
+ if (fs::exists(locpath) && fs::is_directory(locpath))
+ {
+ // directory, iterate over it, skipping subdirs, taking every filename,
+ // sorting them and loading in sorted order
+ fs::directory_iterator it(locpath);
+ std::vector<fs::path> arr;
+ while (it != fs::directory_iterator())
+ {
+ if (!fs::is_directory(*it))
+ arr.push_back(*it);
+ ++it;
+ }
+ std::sort(arr.begin(), arr.end());
+ for (std::vector<fs::path>::iterator i= arr.begin(); i != arr.end(); ++i)
+ {
+ load_rcfile(*i, true);
+ }
+ return; // directory read, skip the rest ...
+ }
+ }
data dat;
L(F("opening rcfile '%s' ...\n") % rc);
read_data_for_command_line(rc, dat);
============================================================
--- monotone.texi 28008d0dc7e6abd7e2e40cab143add9c4faecbca
+++ monotone.texi 131933a2c2cd95c13bf8e3b8c988b8e4bc97664d
@@ -77,6 +77,7 @@ @top Top
* Hook Reference:: Functions which extend monotone
* Special Topics:: Extra explanations and details
* Man Page:: That other document
+* Default hooks:: The standard hook definitions
* Index:: Index of concepts and functions
@end menu
@@ -5280,10 +5281,27 @@ @chapter Hook Reference
using the @option{--rcfile=@var{file}} option; hooks defined in files
specified on the command-line will shadow hooks from the the automatic
files.
+By specifying @option{--rcfile=@var{directory}} you can automatically
+load all the files contained into @var{directory}.
-The remainder of this section documents the existing hook functions
-and their default definitions.
+Monotone also makes available to hook writers a number of helper
+functions exposing functionality not available with standard lua.
+For the complete source of the default hooks see @ref{Default
+hooks}.
+
+@menu
+* Hooks:: All hooks called by monotone.
+* Additional Lua Functions:: Extra functionality availabe to hook writers.
+@end menu
+
+@page
+@node Hooks
+@section Hooks
+
+This section documents the existing hook functions and their default
+definitions.
+
@ftable @code
@item note_commit (@var{new_id}, @var{certs})
@@ -5380,46 +5398,8 @@ @chapter Hook Reference
a successful commit, the contents of @file{MT/log} are erased setting
the system up for another edit/commit cycle.
-The default definition of this hook is:
+For the default definition of this hook, see @ref{Default hooks}.
-@smallexample
-@group
-function edit_comment(commentary, user_log_message)
- local exe = "vi"
- local visual = os.getenv("VISUAL")
- if (visual ~= nil) then exe = visual end
- local editor = os.getenv("EDITOR")
- if (editor ~= nil) then exe = editor end
-
- local tmp, tname = temp_file()
- if (tmp == nil) then return nil end
- commentary = "MT: " .. string.gsub(commentary, "\n", "\nMT: ")
- tmp:write(user_log_message)
- tmp:write(commentary)
- io.close(tmp)
-
- if (os.execute(string.format("%s %s", exe, tname)) ~= 0) then
- os.remove(tname)
- return nil
- end
-
- tmp = io.open(tname, "r")
- if (tmp == nil) then os.remove(tname); return nil end
- local res = ""
- local line = tmp:read()
- while(line ~= nil) do
- if (not string.find(line, "^MT:")) then
- res = res .. line .. "\n"
- end
- line = tmp:read()
- end
- io.close(tmp)
- os.remove(tname)
- return res
-end
-@end group
-@end smallexample
-
@item persist_phrase_ok ()
Returns @code{true} if you want monotone to remember the passphrase of
@@ -5520,34 +5500,9 @@ @chapter Hook Reference
Returns @code{true} if @var{filename} should be ignored while adding,
dropping, or moving files. Otherwise returns @code{false}. This is
most important when performing recursive actions on directories, which
-may affect multiple files simultaneously. The default definition of
-this hook is:
+may affect multiple files simultaneously.
+For the default definition of this hook, see @ref{Default hooks}.
-@smallexample
-@group
-function ignore_file(name)
- if (string.find(name, "%.a$")) then return true end
- if (string.find(name, "%.so$")) then return true end
- if (string.find(name, "%.o$")) then return true end
- if (string.find(name, "%.la$")) then return true end
- if (string.find(name, "%.lo$")) then return true end
- if (string.find(name, "%.aux$")) then return true end
- if (string.find(name, "%.bak$")) then return true end
- if (string.find(name, "%.orig$")) then return true end
- if (string.find(name, "%.rej$")) then return true end
- if (string.find(name, "%~$")) then return true end
- if (string.find(name, "/core$")) then return true end
- if (string.find(name, "^CVS/")) then return true end
- if (string.find(name, "/CVS/")) then return true end
- if (string.find(name, "^%.svn/")) then return true end
- if (string.find(name, "/%.svn/")) then return true end
- if (string.find(name, "^SCCS/")) then return true end
- if (string.find(name, "/SCCS/")) then return true end
- return false;
-end
-@end group
-@end smallexample
-
@item ignore_branch (@var{branchname})
Returns @code{true} if @var{branchname} should be ignored while listing
@@ -5646,61 +5601,10 @@ @chapter Hook Reference
strings, which are the contents of the @var{left} and @var{right}
nodes of a file fork which monotone was unable to automatically
merge. The merge should either call an intelligent merge program or
-interact with the user. The default definition of this hook is:
+interact with the user.
+For the default definition of this hook, see @ref{Default hooks}.
-@smallexample
-@group
-function merge2 (left_path, right_path, merged_path, left, right)
- local ret = nil
- local tbl = @{@}
-
- tbl.lfile = nil
- tbl.rfile = nil
- tbl.outfile = nil
- tbl.meld_exists = false
-
- tbl.lfile = write_to_temporary_file (left)
- tbl.rfile = write_to_temporary_file (right)
- tbl.outfile = write_to_temporary_file ("")
-
- if tbl.lfile ~= nil and tbl.rfile ~= nil and tbl.outfile ~= nil
- then
- tbl.left_path = left_path
- tbl.right_path = right_path
- tbl.merged_path = merged_path
-
- local cmd = get_preferred_merge2_command (tbl)
-
- if cmd ~=nil
- then
- io.write (
- string.format("executing external 2-way merge command\n"))
- cmd ()
- if tbl.meld_exists
- then
- ret = read_contents_of_file (tbl.lfile)
- else
- ret = read_contents_of_file (tbl.outfile)
- end
- if string.len (ret) == 0
- then
- ret = nil
- end
- else
- io.write ("no external 2-way merge command found\n")
- end
- end
-
- os.remove (tbl.lfile)
- os.remove (tbl.rfile)
- os.remove (tbl.outfile)
-
- return ret
-end
-@end group
-@end smallexample
-
@anchor{get_preferred_merge2_command}
@item get_preferred_merge2_command(@var{tbl})
@@ -5710,58 +5614,7 @@ @chapter Hook Reference
that you would like to use to perform merge2 operations, override
this hook to specify it.
-@smallexample
-@group
-function get_preferred_merge2_command (tbl)
- local cmd = nil
- local left_path = tbl.left_path
- local right_path = tbl.right_path
- local merged_path = tbl.merged_path
- local lfile = tbl.lfile
- local rfile = tbl.rfile
- local outfile = tbl.outfile
-
- local editor = string.lower(os.getenv("EDITOR"))
-
-
- if program_exists_in_path("kdiff3") then
- cmd = merge2_kdiff3_cmd (left_path, right_path, merged_path,
- lfile, rfile, outfile)
- elseif program_exists_in_path ("meld") then
- tbl.meld_exists = true
- io.write (string.format(
- "\nWARNING: 'meld' was choosen to perform external 2-way merge.\n"..
- "You should merge all changes to *LEFT* file due to limitation of program\n"..
- "arguments.\n\n"))
- cmd = merge2_meld_cmd (lfile, rfile)
- elseif program_exists_in_path ("xxdiff") then
- cmd = merge2_xxdiff_cmd (left_path, right_path, merged_path,
- lfile, rfile, outfile)
- else
- if string.find(editor, "emacs") ~= nil
- or string.find(editor, "gnu") ~= nil
- then
- if program_exists_in_path ("emacs") then
- cmd = merge2_emacs_cmd ("emacs", lfile, rfile, outfile)
- elseif program_exists_in_path ("xemacs") then
- cmd = merge2_emacs_cmd ("xemacs", lfile, rfile, outfile)
- end
- else if string.find(editor, "vim") ~= nil then
- if os.getenv ("DISPLAY") ~= nil
- and program_exists_in_path ("gvim")
- then
- cmd = merge2_vim_cmd ("gvim", lfile, rfile, outfile)
- elseif program_exists_in_path ("vim") then
- cmd = merge2_vim_cmd ("vim", lfile, rfile, outfile)
- end
- end
- end
- return cmd
-end
-@end group
-@end smallexample
-
@anchor{merge3}
@item merge3 (@var{ancestor}, @var{left}, @var{right})
@@ -5769,65 +5622,10 @@ @chapter Hook Reference
strings, which are the contents of @var{left} and @var{right} nodes,
and least common @var{ancestor}, of a file fork which monotone was
unable to automatically merge. This hook delegates the actual merge
-to the result of @ref{get_preferred_merge3_command}. The default
-definition of this hook is:
+to the result of @ref{get_preferred_merge3_command}.
+For the default definition of this hook, see @ref{Default hooks}.
-@smallexample
-@group
-function merge3 (anc_path, left_path, right_path, merged_path,
- ancestor, left, right)
- local ret
- local tbl = @{@}
-
- tbl.anc_path = anc_path
- tbl.left_path = left_path
- tbl.right_path = right_path
- tbl.merged_path = merged_path
- tbl.afile = nil
- tbl.lfile = nil
- tbl.rfile = nil
- tbl.outfile = nil
- tbl.meld_exists = false
- tbl.lfile = write_to_temporary_file (left)
- tbl.afile = write_to_temporary_file (ancestor)
- tbl.rfile = write_to_temporary_file (right)
- tbl.outfile = write_to_temporary_file ("")
-
- if tbl.lfile ~= nil and tbl.rfile ~= nil
- and tbl.afile ~= nil and tbl.outfile ~= nil
- then
- local cmd = get_preferred_merge3_command (tbl)
- if cmd ~=nil
- then
- io.write (string.format(
- "executing external 3-way merge command\n"))
- cmd ()
- if tbl.meld_exists
- then
- ret = read_contents_of_file (tbl.afile)
- else
- ret = read_contents_of_file (tbl.outfile)
- end
- if string.len (ret) == 0
- then
- ret = nil
- end
- else
- io.write ("no external 3-way merge command found\n")
- end
- end
-
- os.remove (tbl.lfile)
- os.remove (tbl.rfile)
- os.remove (tbl.afile)
- os.remove (tbl.outfile)
-
- return ret
-end
-@end group
-@end smallexample
-
@anchor{get_preferred_merge3_command}
@item get_preferred_merge3_command(@var{tbl})
@@ -5837,65 +5635,6 @@ @chapter Hook Reference
that you would like to use to perform merge3 operations, override
this hook to specify it.
-@smallexample
-@group
-function get_preferred_merge3_command (tbl)
- local cmd = nil
- local left_path = tbl.left_path
- local anc_path = tbl.anc_path
- local right_path = tbl.right_path
- local merged_path = tbl.merged_path
- local lfile = tbl.lfile
- local afile = tbl.afile
- local rfile = tbl.rfile
- local outfile = tbl.outfile
-
- local editor = string.lower(os.getenv("EDITOR"))
-
- if program_exists_in_path("kdiff3") then
- cmd = merge3_kdiff3_cmd (left_path, anc_path, right_path,
- merged_path, lfile, afile, rfile, outfile)
- elseif program_exists_in_path ("meld") then
- tbl.meld_exists = true
- io.write (string.format(
- "\nWARNING: 'meld' was choosen to perform external 3-way merge.\n"..
- "You should merge all changes to *CENTER* file due to limitation of program\n"..
- "arguments.\n\n"))
- cmd = merge3_meld_cmd (lfile, afile, rfile)
- elseif program_exists_in_path ("xxdiff") then
- cmd = merge3_xxdiff_cmd (left_path, anc_path, right_path,
- merged_path, lfile, afile, rfile, outfile)
- else
- -- prefer emacs/xemacs
- if string.find(editor, "emacs") ~= nil
- or string.find(editor, "gnu") ~= nil
- then
- if program_exists_in_path ("xemacs") then
- cmd = merge3_emacs_cmd ("xemacs", lfile, afile,
- rfile, outfile)
- elseif program_exists_in_path ("emacs") then
- cmd = merge3_emacs_cmd ("emacs", lfile, afile,
- rfile, outfile)
- end
- elseif string.find(editor, "vim") ~= nil then -- prefer vim
- if os.getenv ("DISPLAY") ~= nil
- and program_exists_in_path ("gvim")
- then
- cmd = merge3_vim_cmd ("gvim", lfile, afile,
- rfile, outfile)
- elseif program_exists_in_path ("vim") then
- cmd = merge3_vim_cmd ("vim", lfile, afile,
- rfile, outfile)
- end
- end
- end
-
- return cmd
-end
-@end group
-@end smallexample
-
-
@item expand_selector (@var{str})
Attempts to expand @var{str} as a selector. Expansion generally means
@@ -5903,115 +5642,18 @@ @chapter Hook Reference
authors or @code{d:} for dates. Expansion may also mean recognizing
and interpreting special words such as @code{yesterday} or @code{6
months ago} and converting them into well formed selectors. For more
-detail on the use of selectors, see @ref{Selectors}. The default
-definition of this hook is:
+detail on the use of selectors, see @ref{Selectors}.
+For the default definition of this hook, see @ref{Default hooks}.
-@smallexample
-@group
-function expand_selector(str)
-
- -- something which looks like a generic cert pattern
- if string.find(str, "^[^=]*=.*$")
- then
- return ("c:" .. str)
- end
-
- -- something which looks like an email address
- if string.find(str, "[%w%-_]+@@[%w%-_]+")
- then
- return ("a:" .. str)
- end
-
- -- something which looks like a branch name
- if string.find(str, "[%w%-]+%.[%w%-]+")
- then
- return ("b:" .. str)
- end
-
- -- a sequence of nothing but hex digits
- if string.find(str, "^%x+$")
- then
- return ("i:" .. str)
- end
-
- -- tries to expand as a date
- local dtstr = expand_date(str)
- if dtstr ~= nil
- then
- return ("d:" .. dtstr)
- end
-
- return nil
-end
-@end group
-@end smallexample
-
@item expand_date (@var{str})
Attempts to expand @var{str} as a date expression. Expansion means recognizing
and interpreting special words such as @code{yesterday} or @code{6
months ago} and converting them into well formed date expressions. For more
-detail on the use of selectors, see @ref{Selectors}. The default
-definition of this hook is:
+detail on the use of selectors, see @ref{Selectors}.
+For the default definition of this hook, see @ref{Default hooks}.
-@smallexample
-@group
-function expand_date(str)
- -- simple date patterns
- if string.find(str, "^19%d%d%-%d%d")
- or string.find(str, "^20%d%d%-%d%d")
- then
- return (str)
- end
- -- "now"
- if str == "now"
- then
- local t = os.time(os.date('!*t'))
- return os.date("%FT%T", t)
- end
-
- -- today don't uses the time
- if str == "today"
- then
- local t = os.time(os.date('!*t'))
- return os.date("%F", t)
- end
-
- -- "yesterday", the source of all hangovers
- if str == "yesterday"
- then
- local t = os.time(os.date('!*t'))
- return os.date("%F", t - 86400)
- end
-
- -- "CVS style" relative dates such as "3 weeks ago"
- local trans = @{
- minute = 60;
- hour = 3600;
- day = 86400;
- week = 604800;
- month = 2678400;
- year = 31536000
- @}
- local pos, len, n, type = string.find(str, "(%d+) ([minutehordaywk]+)s? ago")
- if trans[type] ~= nil
- then
- local t = os.time(os.date('!*t'))
- if trans[type] <= 3600
- then
- return os.date("%FT%T", t - (n * trans[type]))
- else
- return os.date("%F", t - (n * trans[type]))
- end
- end
-
- return nil
-end
-@end group
-@end smallexample
-
-
@item get_system_linesep ()
Returns a string which defines the default system line separator.
@@ -6133,41 +5775,124 @@ @chapter Hook Reference
@end group
@end smallexample
-The @code{binary_file} function is also defined as a lua hook as
-follows:
-
+The @code{binary_file} function is also defined as a lua hook. See
+@ref{Default hooks}.
+
+@end ftable
+
+@page
+@node Additional Lua Functions
+@section Additional Lua Functions
+
+This section documents the additional lua functions made available to
+hook writers.
+
+@ftable @code
+
+@item existonpath(@var{possible_command})
+
+This function receives a string containing the name of an external
+program and returns 0 if it exists on path and is executable, -1
+otherwise.
+As an example, @code{existonpath("xxdiff")} returns 0 if the
+program xxdiff is available.
+On windows, this function automatically appends ``.exe'' to the
+program name. In the previous example, @code{existonpath} would search
+for ``xxdiff.exe''.
+
+@item guess_binary(@var{filespec})
+
+Returns true if the file appears to be binary, i.e. contains one or
+more of the following characters:
@smallexample
@group
-function binary_file(name)
- local lowname=string.lower(name)
- -- some known binaries, return true
- if (string.find(lowname, "%.gif$")) then return true end
- if (string.find(lowname, "%.jpe?g$")) then return true end
- if (string.find(lowname, "%.png$")) then return true end
- if (string.find(lowname, "%.bz2$")) then return true end
- if (string.find(lowname, "%.gz$")) then return true end
- if (string.find(lowname, "%.zip$")) then return true end
- -- some known text, return false
- if (string.find(lowname, "%.cc?$")) then return false end
- if (string.find(lowname, "%.cxx$")) then return false end
- if (string.find(lowname, "%.hh?$")) then return false end
- if (string.find(lowname, "%.hxx$")) then return false end
- if (string.find(lowname, "%.lua$")) then return false end
- if (string.find(lowname, "%.texi$")) then return false end
- if (string.find(lowname, "%.sql$")) then return false end
- -- unknown - read file and use the guess-binary built-in
- -- monotone function
- filedata=read_contents_of_file(name)
- if (filedata ~= nil) then return guess_binary(filedata) end
- -- if still unknown, treat as binary
- return true
-end
+0x00 thru 0x06
+0x0E thru 0x1a
+0x1c thru 0x1f
@end group
@end smallexample
+@item include(@var{scriptfile})
+This function tries to load and execute the script contained into
+scriptfile. It returns true for success and false if there is an
+error.
+
+@item includedir(@var{scriptpath})
+
+This function loads and executes in alphabetical order all the scripts
+contained into the directory scriptpath.
+If one of the scripts has an error, the functions doesn't process the
+remaining scripts and immediately returns false.
+
+@item is_executable(@var{filespec})
+
+This function returns true if the file is executable, false
+otherwise. On windows this function returns always false.
+
+@item kill(@var{pid} [, @var{signal}])
+
+This function calls the kill() C library function on posix systems and
+TerminateProcess on Win32 (in that case @var{pid} is the process
+handle). If the optional @var{signal} parameter is missing, SIGTERM
+will be used.
+Returns 0 on succes, -1 on error.
+
+@item make_executable(@var{filespec})
+
+This function marks the named file as executable. On windows has no
+effect.
+
+@item mkstemp(@var{template})
+
+Like its C library counterpart, mkstemp creates a unique name and
+returns a file descriptor for the newly created file.
+The value of template should be a pointer to a character buffer loaded
+with a null-terminated string that consists of contiguous, legal file
+ad path name characters followed by six Xs.
+The function mkstemp replaces the Xs by an alpha-numeric sequence
+that is chosen to ensure that no file in the chosen directory has that
+name.
+Furthermore, subsequent calls to mkstemp within the same process
+each yield different file names.
+Unlike other implementations, monotone mkstemp allows the template
+string to contain a complete path, not only a filename, allowing users
+to create temporary files outside the current directory.
+
+@strong{Important notice:}@*
+To create a temporary file, you must use the @code{temp_file()}
+function, unless you need to run monotone with the @option{--nostd}
+option. @code{temp_file()} builds on @code{mkstemp()} and creates a
+file in the standard TMP/TEMP directories.
+For the definition of @code{temp_file()}, see @ref{Default hooks}.
+
+@item sleep(@var{seconds})
+
+Makes the calling process sleep for the specified number of seconds.
+
+@item spawn(@var{executable} [, @var{args ...}])
+
+Starts the named executable with the given arguments. Returns the
+process pid on Posix systems, the process handle on Win32 or -1 if
+there was an error.
+Calls fork/execvp on Posix, CreateProcess on Win32.
+
+@strong{Important notice:}@*
+To spawn a process and wait for its completion, use the @code{execute()}
+function, unless you need to run monotone with the @option{--nostd}
+option. @code{execute()} builds on @code{spawn()} and @code{wait()}
+in a standardized way.
+
+@item wait(@var{pid})
+
+Wait until the process with given pid (process handle on Win32) exits.
+Returns two values: a result value and the exit code of the waited-for
+process.
+The exit code is meaningful only if the result value is 0.
+
@end ftable
+
@node Special Topics
@chapter Special Topics
@@ -7240,6 +6965,16 @@ @section AUTHOR
graydon hoare <graydon@@pobox.com>
+@node Default hooks
+@appendix Default hooks
+
+This section contains the entire source code of the standard hook file,
+that is built in to the monotone executable, and read before any user
+hooks files (unless @option{--nostd} is passed). It contains the
+default values for all hooks.
+
+@verbatiminclude std_hooks.lua
+
@node Index
@unnumbered Index
============================================================
--- netcmd.cc 3d925448e00e4f5a0e70f9ac977899fbf9312a1e
+++ netcmd.cc 207caa3c268ad7e6a93eaff3f3298aa53f45fadd
@@ -8,6 +8,8 @@
#include <utility>
#include "cryptopp/gzip.h"
+#include "cryptopp/hmac.h"
+#include "cryptopp/sha.h"
#include "adler32.hh"
#include "constants.hh"
@@ -50,9 +52,6 @@ netcmd::netcmd() : version(constants::ne
cmd_code(bye_cmd),
payload_len(0), length_len(0)
{}
-
-netcmd::netcmd(u8 _version) : version(_version), cmd_code(bye_cmd),
- payload_len(0), length_len(0)
{}
size_t netcmd::encoded_size()
@@ -82,61 +81,53 @@ void
}
void
-netcmd::write(string & out) const
+netcmd::write(string & out, netsync_session_key const & key,
+ netsync_hmac_value & hmac_val) const
{
+ size_t oldlen = out.size();
out += static_cast<char>(version);
out += static_cast<char>(cmd_code);
insert_variable_length_string(payload, out);
- adler32 check(reinterpret_cast<u8 const *>(payload.data()), payload.size());
- insert_datum_lsb<u32>(check.sum(), out);
-}
-// last should be zero (doesn't mean we're compatible with version 0).
-// The nonzero elements are the historical netsync/netcmd versions we can
-// interoperate with. For interoperating with newer versions, assume
-// compatibility and let the remote host make the call.
-static u8 const compatible_versions[] = {4, 0};
-
-bool is_compatible(u8 version)
-{
- for (u8 const *x = compatible_versions; *x; ++x)
+ I(key().size() == CryptoPP::SHA::DIGESTSIZE);
+ I(key().size() == hmac_val().size());
+ byte keybuf[CryptoPP::SHA::DIGESTSIZE];
+ for (size_t i = 0; i < sizeof(keybuf); i++)
{
- if (*x == version)
- return true;
+ keybuf[i] = key()[i] ^ hmac_val()[i];
}
- return false;
+ CryptoPP::HMAC<CryptoPP::SHA> hmac(keybuf, sizeof(keybuf));
+ char digest[CryptoPP::SHA::DIGESTSIZE];
+ hmac.CalculateDigest(reinterpret_cast<byte *>(digest),
+ reinterpret_cast<const byte *>(out.data() + oldlen),
+ out.size() - oldlen);
+ string digest_str(digest, sizeof(digest));
+ hmac_val = netsync_hmac_value(digest_str);
+ out.append(digest_str);
}
-
+
bool
-netcmd::read(string & inbuf)
+netcmd::read(string & inbuf, netsync_session_key const & key,
+ netsync_hmac_value & hmac_val)
{
size_t pos = 0;
if (inbuf.size() < constants::netcmd_minsz)
return false;
- u8 ver = extract_datum_lsb<u8>(inbuf, pos, "netcmd protocol number");
- int v = version;
+ u8 extracted_ver = extract_datum_lsb<u8>(inbuf, pos, "netcmd protocol number");
+ if (extracted_ver != version)
+ throw bad_decode(F("protocol version mismatch: wanted '%d' got '%d'")
+ % widen<u32,u8>(version)
+ % widen<u32,u8>(extracted_ver));
+ version = extracted_ver;
u8 cmd_byte = extract_datum_lsb<u8>(inbuf, pos, "netcmd code");
switch (cmd_byte)
{
- // hello may be newer than expected, or one we're compatible with
case static_cast<u8>(hello_cmd):
- if (ver < version && !is_compatible(ver))
- throw bad_decode(F("protocol version mismatch: wanted '%d' got '%d'")
- % widen<u32,u8>(v)
- % widen<u32,u8>(ver));
- break;
- // these may be older compatible versions
case static_cast<u8>(anonymous_cmd):
case static_cast<u8>(auth_cmd):
- if (ver != version && (ver > version || !is_compatible(ver)))
- throw bad_decode(F("protocol version mismatch: wanted '%d' got '%d'")
- % widen<u32,u8>(v)
- % widen<u32,u8>(ver));
- break;
- // these must match exactly what's expected
case static_cast<u8>(error_cmd):
case static_cast<u8>(bye_cmd):
case static_cast<u8>(confirm_cmd):
@@ -147,16 +138,11 @@ netcmd::read(string & inbuf)
case static_cast<u8>(data_cmd):
case static_cast<u8>(delta_cmd):
case static_cast<u8>(nonexistant_cmd):
- if (ver != version)
- throw bad_decode(F("protocol version mismatch: wanted '%d' got '%d'")
- % widen<u32,u8>(v)
- % widen<u32,u8>(ver));
+ cmd_code = static_cast<netcmd_code>(cmd_byte);
break;
default:
throw bad_decode(F("unknown netcmd code 0x%x") % widen<u32,u8>(cmd_byte));
}
- cmd_code = static_cast<netcmd_code>(cmd_byte);
- version = ver;
// check to see if we have even enough bytes for a complete uleb128
size_t old_pos = pos;
@@ -170,9 +156,9 @@ netcmd::read(string & inbuf)
throw bad_decode(F("oversized payload of '%d' bytes") % payload_len);
// there might not be enough data yet in the input buffer
- if (inbuf.size() < pos + payload_len + sizeof(u32))
+ if (inbuf.size() < pos + payload_len + CryptoPP::SHA::DIGESTSIZE)
{
- inbuf.reserve(pos + payload_len + sizeof(u32) + constants::bufsz);
+ inbuf.reserve(pos + payload_len + CryptoPP::SHA::DIGESTSIZE + constants::bufsz);
return false;
}
@@ -192,10 +178,25 @@ netcmd::read(string & inbuf)
length_len = 0;
// they might have given us bogus data
- adler32 check(reinterpret_cast<u8 const *>(payload.data()),
- payload.size());
- if (checksum != check.sum())
- throw bad_decode(F("bad checksum 0x%x vs. 0x%x") % checksum % check.sum());
+ string cmd_digest = extract_substring(inbuf, pos, CryptoPP::SHA::DIGESTSIZE,
+ "netcmd HMAC");
+ I(key().size() == CryptoPP::SHA::DIGESTSIZE);
+ I(key().size() == hmac_val().size());
+ byte keybuf[CryptoPP::SHA::DIGESTSIZE];
+ for (size_t i = 0; i < sizeof(keybuf); i++)
+ {
+ keybuf[i] = key()[i] ^ hmac_val()[i];
+ }
+ CryptoPP::HMAC<CryptoPP::SHA> hmac(keybuf, sizeof(keybuf));
+ char digest_buf[CryptoPP::SHA::DIGESTSIZE];
+ hmac.CalculateDigest(reinterpret_cast<byte *>(digest_buf),
+ reinterpret_cast<const byte *>(payload.data()),
+ payload.size());
+ string digest(digest_buf, sizeof(digest_buf));
+ if (cmd_digest != digest)
+ throw bad_decode(F("bad HMAC %s vs. %s") % encode_hexenc(cmd_digest)
+ % encode_hexenc(digest));
+ hmac_val = netsync_hmac_value(digest);
return true;
}
@@ -256,37 +257,37 @@ netcmd::write_hello_cmd(rsa_keypair_id c
}
-void
-netcmd::read_anonymous_cmd(protocol_role & role,
+void
+netcmd::read_anonymous_cmd(protocol_role & role,
std::string & pattern,
- id & nonce2) const
+ rsa_oaep_sha_data & hmac_key_encrypted) const
{
size_t pos = 0;
- // syntax is: <role:1 byte> <pattern: vstr> <nonce2: 20 random bytes>
- u8 role_byte = extract_datum_lsb<u8>(payload, pos, "anonymous netcmd, role");
+ // syntax is: <role:1 byte> <pattern: vstr> <hmac_key_encrypted: vstr>
+ u8 role_byte = extract_datum_lsb<u8>(payload, pos, "anonymous(hmac) netcmd, role");
if (role_byte != static_cast<u8>(source_role)
&& role_byte != static_cast<u8>(sink_role)
&& role_byte != static_cast<u8>(source_and_sink_role))
throw bad_decode(F("unknown role specifier %d") % widen<u32,u8>(role_byte));
role = static_cast<protocol_role>(role_byte);
extract_variable_length_string(payload, pattern, pos,
- "anonymous netcmd, pattern");
- nonce2 = id(extract_substring(payload, pos,
- constants::merkle_hash_length_in_bytes,
- "anonymous netcmd, nonce2"));
- assert_end_of_buffer(payload, pos, "anonymous netcmd payload");
+ "anonymous(hmac) netcmd, pattern");
+ string hmac_key_string;
+ extract_variable_length_string(payload, hmac_key_string, pos,
+ "anonymous(hmac) netcmd, hmac_key_encrypted");
+ hmac_key_encrypted = rsa_oaep_sha_data(hmac_key_string);
+ assert_end_of_buffer(payload, pos, "anonymous(hmac) netcmd payload");
}
-void
-netcmd::write_anonymous_cmd(protocol_role role,
+void
+netcmd::write_anonymous_cmd(protocol_role role,
std::string const & pattern,
- id const & nonce2)
+ rsa_oaep_sha_data const & hmac_key_encrypted)
{
cmd_code = anonymous_cmd;
- I(nonce2().size() == constants::merkle_hash_length_in_bytes);
payload = static_cast<char>(role);
insert_variable_length_string(pattern, payload);
- payload += nonce2();
+ insert_variable_length_string(hmac_key_encrypted(), payload);
}
void
@@ -294,74 +295,68 @@ netcmd::read_auth_cmd(protocol_role & ro
string & pattern,
id & client,
id & nonce1,
- id & nonce2,
+ rsa_oaep_sha_data & hmac_key_encrypted,
string & signature) const
{
size_t pos = 0;
// syntax is: <role:1 byte> <pattern: vstr>
- // <client: 20 bytes sha1> <nonce1: 20 random bytes> <nonce2: 20 random bytes>
- // <signature: vstr>
+ // <client: 20 bytes sha1> <nonce1: 20 random bytes>
+ // <hmac_key_encrypted: vstr> <signature: vstr>
u8 role_byte = extract_datum_lsb<u8>(payload, pos, "auth netcmd, role");
if (role_byte != static_cast<u8>(source_role)
&& role_byte != static_cast<u8>(sink_role)
&& role_byte != static_cast<u8>(source_and_sink_role))
throw bad_decode(F("unknown role specifier %d") % widen<u32,u8>(role_byte));
role = static_cast<protocol_role>(role_byte);
- extract_variable_length_string(payload, pattern, pos, "auth netcmd, pattern");
+ extract_variable_length_string(payload, pattern, pos, "auth(hmac) netcmd, pattern");
client = id(extract_substring(payload, pos,
constants::merkle_hash_length_in_bytes,
- "auth netcmd, client identifier"));
+ "auth(hmac) netcmd, client identifier"));
nonce1 = id(extract_substring(payload, pos,
constants::merkle_hash_length_in_bytes,
- "auth netcmd, nonce1"));
- nonce2 = id(extract_substring(payload, pos,
- constants::merkle_hash_length_in_bytes,
- "auth netcmd, nonce2"));
+ "auth(hmac) netcmd, nonce1"));
+ string hmac_key;
+ extract_variable_length_string(payload, hmac_key, pos,
+ "auth(hmac) netcmd, hmac_key_encrypted");
+ hmac_key_encrypted = rsa_oaep_sha_data(hmac_key);
extract_variable_length_string(payload, signature, pos,
- "auth netcmd, signature");
- assert_end_of_buffer(payload, pos, "auth netcmd payload");
+ "auth(hmac) netcmd, signature");
+ assert_end_of_buffer(payload, pos, "auth(hmac) netcmd payload");
}
-void
-netcmd::write_auth_cmd(protocol_role role,
- string const & pattern,
+void
+netcmd::write_auth_cmd(protocol_role role,
+ string const & pattern,
id const & client,
- id const & nonce1,
- id const & nonce2,
+ id const & nonce1,
+ rsa_oaep_sha_data const & hmac_key_encrypted,
string const & signature)
{
cmd_code = auth_cmd;
I(client().size() == constants::merkle_hash_length_in_bytes);
I(nonce1().size() == constants::merkle_hash_length_in_bytes);
- I(nonce2().size() == constants::merkle_hash_length_in_bytes);
payload = static_cast<char>(role);
insert_variable_length_string(pattern, payload);
payload += client();
payload += nonce1();
- payload += nonce2();
+ insert_variable_length_string(hmac_key_encrypted(), payload);
insert_variable_length_string(signature, payload);
}
-
-void
-netcmd::read_confirm_cmd(string & signature) const
+void
+netcmd::read_confirm_cmd() const
{
size_t pos = 0;
-
- // syntax is: <signature: vstr>
- extract_variable_length_string(payload, signature, pos,
- "confirm netcmd, signature");
assert_end_of_buffer(payload, pos, "confirm netcmd payload");
}
-void
-netcmd::write_confirm_cmd(string const & signature)
+void
+netcmd::write_confirm_cmd()
{
cmd_code = confirm_cmd;
payload.clear();
- insert_variable_length_string(signature, payload);
}
-
+
void
netcmd::read_refine_cmd(merkle_node & node) const
{
@@ -577,6 +572,65 @@ netcmd::write_nonexistant_cmd(netcmd_ite
#include "transforms.hh"
#include <boost/lexical_cast.hpp>
+void
+test_netcmd_mac()
+{
+ netcmd out_cmd, in_cmd;
+ string buf;
+ netsync_session_key key(constants::netsync_key_initializer);
+ {
+ netsync_hmac_value mac(constants::netsync_key_initializer);
+ // mutates mac
+ out_cmd.write(buf, key, mac);
+ BOOST_CHECK_THROW(in_cmd.read(buf, key, mac), bad_decode);
+ }
+
+ {
+ netsync_hmac_value mac(constants::netsync_key_initializer);
+ out_cmd.write(buf, key, mac);
+ }
+ buf[0] ^= 0xff;
+ {
+ netsync_hmac_value mac(constants::netsync_key_initializer);
+ BOOST_CHECK_THROW(in_cmd.read(buf, key, mac), bad_decode);
+ }
+
+ {
+ netsync_hmac_value mac(constants::netsync_key_initializer);
+ out_cmd.write(buf, key, mac);
+ }
+ buf[buf.size() - 1] ^= 0xff;
+ {
+ netsync_hmac_value mac(constants::netsync_key_initializer);
+ BOOST_CHECK_THROW(in_cmd.read(buf, key, mac), bad_decode);
+ }
+
+ {
+ netsync_hmac_value mac(constants::netsync_key_initializer);
+ out_cmd.write(buf, key, mac);
+ }
+ buf += '\0';
+ {
+ netsync_hmac_value mac(constants::netsync_key_initializer);
+ BOOST_CHECK_THROW(in_cmd.read(buf, key, mac), bad_decode);
+ }
+}
+
+static void
+do_netcmd_roundtrip(netcmd const & out_cmd, netcmd & in_cmd, string & buf)
+{
+ netsync_session_key key(constants::netsync_key_initializer);
+ {
+ netsync_hmac_value mac(constants::netsync_key_initializer);
+ out_cmd.write(buf, key, mac);
+ }
+ {
+ netsync_hmac_value mac(constants::netsync_key_initializer);
+ BOOST_CHECK(in_cmd.read(buf, key, mac));
+ }
+ BOOST_CHECK(in_cmd == out_cmd);
+}
+
void
test_netcmd_functions()
{
@@ -591,10 +645,8 @@ test_netcmd_functions()
string out_errmsg("your shoelaces are untied"), in_errmsg;
string buf;
out_cmd.write_error_cmd(out_errmsg);
- out_cmd.write(buf);
- BOOST_CHECK(in_cmd.read(buf));
+ do_netcmd_roundtrip(out_cmd, in_cmd, buf);
in_cmd.read_error_cmd(in_errmsg);
- BOOST_CHECK(in_cmd == out_cmd);
BOOST_CHECK(in_errmsg == out_errmsg);
L(F("errmsg_cmd test done, buffer was %d bytes\n") % buf.size());
}
@@ -604,9 +656,7 @@ test_netcmd_functions()
L(F("checking i/o round trip on bye_cmd\n"));
netcmd out_cmd, in_cmd;
string buf;
- out_cmd.write(buf);
- BOOST_CHECK(in_cmd.read(buf));
- BOOST_CHECK(in_cmd == out_cmd);
+ do_netcmd_roundtrip(out_cmd, in_cmd, buf);
L(F("bye_cmd test done, buffer was %d bytes\n") % buf.size());
}
@@ -619,10 +669,8 @@ test_netcmd_functions()
rsa_pub_key out_server_key("9387938749238792874"), in_server_key;
id out_nonce(raw_sha1("nonce it up")), in_nonce;
out_cmd.write_hello_cmd(out_server_keyname, out_server_key, out_nonce);
- out_cmd.write(buf);
- BOOST_CHECK(in_cmd.read(buf));
+ do_netcmd_roundtrip(out_cmd, in_cmd, buf);
in_cmd.read_hello_cmd(in_server_keyname, in_server_key, in_nonce);
- BOOST_CHECK(in_cmd == out_cmd);
BOOST_CHECK(in_server_keyname == out_server_keyname);
BOOST_CHECK(in_server_key == out_server_key);
BOOST_CHECK(in_nonce == out_nonce);
@@ -635,15 +683,15 @@ test_netcmd_functions()
netcmd out_cmd, in_cmd;
protocol_role out_role = source_and_sink_role, in_role;
string buf;
- id out_nonce2(raw_sha1("nonce start my heart")), in_nonce2;
+ // total cheat, since we don't actually verify that rsa_oaep_sha_data
+ // is sensible anywhere here...
+ rsa_oaep_sha_data out_key("nonce start my heart"), in_key;
string out_pattern("radishes galore!"), in_pattern;
- out_cmd.write_anonymous_cmd(out_role, out_pattern, out_nonce2);
- out_cmd.write(buf);
- BOOST_CHECK(in_cmd.read(buf));
- in_cmd.read_anonymous_cmd(in_role, in_pattern, in_nonce2);
- BOOST_CHECK(in_cmd == out_cmd);
- BOOST_CHECK(in_nonce2 == out_nonce2);
+ out_cmd.write_anonymous_cmd(out_role, out_pattern, out_key);
+ do_netcmd_roundtrip(out_cmd, in_cmd, buf);
+ in_cmd.read_anonymous_cmd(in_role, in_pattern, in_key);
+ BOOST_CHECK(in_key == out_key);
BOOST_CHECK(in_role == out_role);
L(F("anonymous_cmd test done, buffer was %d bytes\n") % buf.size());
}
@@ -655,21 +703,21 @@ test_netcmd_functions()
protocol_role out_role = source_and_sink_role, in_role;
string buf;
id out_client(raw_sha1("happy client day")), out_nonce1(raw_sha1("nonce me amadeus")),
- out_nonce2(raw_sha1("nonce start my heart")),
- in_client, in_nonce1, in_nonce2;
+ in_client, in_nonce1;
+ // total cheat, since we don't actually verify that rsa_oaep_sha_data
+ // is sensible anywhere here...
+ rsa_oaep_sha_data out_key("nonce start my heart"), in_key;
string out_signature(raw_sha1("burble") + raw_sha1("gorby")), out_pattern("radishes galore!"),
in_signature, in_pattern;
out_cmd.write_auth_cmd(out_role, out_pattern, out_client, out_nonce1,
- out_nonce2, out_signature);
- out_cmd.write(buf);
- BOOST_CHECK(in_cmd.read(buf));
+ out_key, out_signature);
+ do_netcmd_roundtrip(out_cmd, in_cmd, buf);
in_cmd.read_auth_cmd(in_role, in_pattern, in_client,
- in_nonce1, in_nonce2, in_signature);
- BOOST_CHECK(in_cmd == out_cmd);
+ in_nonce1, in_key, in_signature);
BOOST_CHECK(in_client == out_client);
BOOST_CHECK(in_nonce1 == out_nonce1);
- BOOST_CHECK(in_nonce2 == out_nonce2);
+ BOOST_CHECK(in_key == out_key);
BOOST_CHECK(in_signature == out_signature);
BOOST_CHECK(in_role == out_role);
L(F("auth_cmd test done, buffer was %d bytes\n") % buf.size());
@@ -680,14 +728,9 @@ test_netcmd_functions()
L(F("checking i/o round trip on confirm_cmd\n"));
netcmd out_cmd, in_cmd;
string buf;
- string out_signature(raw_sha1("egg") + raw_sha1("tomago")), in_signature;
-
- out_cmd.write_confirm_cmd(out_signature);
- out_cmd.write(buf);
- BOOST_CHECK(in_cmd.read(buf));
- in_cmd.read_confirm_cmd(in_signature);
- BOOST_CHECK(in_cmd == out_cmd);
- BOOST_CHECK(in_signature == out_signature);
+ out_cmd.write_confirm_cmd();
+ do_netcmd_roundtrip(out_cmd, in_cmd, buf);
+ in_cmd.read_confirm_cmd();
L(F("confirm_cmd test done, buffer was %d bytes\n") % buf.size());
}
@@ -708,10 +751,8 @@ test_netcmd_functions()
out_node.set_slot_state(15, subtree_state);
out_cmd.write_refine_cmd(out_node);
- out_cmd.write(buf);
- BOOST_CHECK(in_cmd.read(buf));
+ do_netcmd_roundtrip(out_cmd, in_cmd, buf);
in_cmd.read_refine_cmd(in_node);
- BOOST_CHECK(in_cmd == out_cmd);
BOOST_CHECK(in_node == out_node);
L(F("refine_cmd test done, buffer was %d bytes\n") % buf.size());
}
@@ -725,8 +766,7 @@ test_netcmd_functions()
string buf;
out_cmd.write_done_cmd(out_level, out_type);
- out_cmd.write(buf);
- BOOST_CHECK(in_cmd.read(buf));
+ do_netcmd_roundtrip(out_cmd, in_cmd, buf);
in_cmd.read_done_cmd(in_level, in_type);
BOOST_CHECK(in_level == out_level);
BOOST_CHECK(in_type == out_type);
@@ -742,8 +782,7 @@ test_netcmd_functions()
string buf;
out_cmd.write_send_data_cmd(out_type, out_id);
- out_cmd.write(buf);
- BOOST_CHECK(in_cmd.read(buf));
+ do_netcmd_roundtrip(out_cmd, in_cmd, buf);
in_cmd.read_send_data_cmd(in_type, in_id);
BOOST_CHECK(in_type == out_type);
BOOST_CHECK(in_id == out_id);
@@ -760,8 +799,7 @@ test_netcmd_functions()
string buf;
out_cmd.write_send_delta_cmd(out_type, out_head, out_base);
- out_cmd.write(buf);
- BOOST_CHECK(in_cmd.read(buf));
+ do_netcmd_roundtrip(out_cmd, in_cmd, buf);
in_cmd.read_send_delta_cmd(in_type, in_head, in_base);
BOOST_CHECK(in_type == out_type);
BOOST_CHECK(in_head == out_head);
@@ -778,8 +816,7 @@ test_netcmd_functions()
string out_dat("thank you for flying northwest"), in_dat;
string buf;
out_cmd.write_data_cmd(out_type, out_id, out_dat);
- out_cmd.write(buf);
- BOOST_CHECK(in_cmd.read(buf));
+ do_netcmd_roundtrip(out_cmd, in_cmd, buf);
in_cmd.read_data_cmd(in_type, in_id, in_dat);
BOOST_CHECK(in_id == out_id);
BOOST_CHECK(in_dat == out_dat);
@@ -797,8 +834,7 @@ test_netcmd_functions()
string buf;
out_cmd.write_delta_cmd(out_type, out_head, out_base, out_delta);
- out_cmd.write(buf);
- BOOST_CHECK(in_cmd.read(buf));
+ do_netcmd_roundtrip(out_cmd, in_cmd, buf);
in_cmd.read_delta_cmd(in_type, in_head, in_base, in_delta);
BOOST_CHECK(in_type == out_type);
BOOST_CHECK(in_head == out_head);
@@ -816,8 +852,7 @@ test_netcmd_functions()
string buf;
out_cmd.write_nonexistant_cmd(out_type, out_id);
- out_cmd.write(buf);
- BOOST_CHECK(in_cmd.read(buf));
+ do_netcmd_roundtrip(out_cmd, in_cmd, buf);
in_cmd.read_nonexistant_cmd(in_type, in_id);
BOOST_CHECK(in_type == out_type);
BOOST_CHECK(in_id == out_id);
@@ -836,6 +871,7 @@ add_netcmd_tests(test_suite * suite)
add_netcmd_tests(test_suite * suite)
{
suite->add(BOOST_TEST_CASE(&test_netcmd_functions));
+ suite->add(BOOST_TEST_CASE(&test_netcmd_mac));
}
#endif // BUILD_UNIT_TESTS
============================================================
--- netcmd.hh 901532e162e97d3df8f89ccc1198203eef8ed8fe
+++ netcmd.hh b010800c5f7c9cb23ac8f0ac069b7a4fbdd17774
@@ -65,8 +65,11 @@ public:
// basic cmd i/o (including checksums)
- void write(std::string & out) const;
- bool read(std::string & inbuf);
+ void write(std::string & out,
+ netsync_session_key const & key,
+ netsync_hmac_value & hmac_val) const;
+ bool read(std::string & inbuf,
+ netsync_session_key const & key, netsync_hmac_value & hmac_val);
// i/o functions for each type of command payload
void read_error_cmd(std::string & errmsg) const;
@@ -82,28 +85,28 @@ public:
rsa_pub_key const & server_key,
id const & nonce);
- void read_anonymous_cmd(protocol_role & role,
+ void read_anonymous_cmd(protocol_role & role,
std::string & pattern,
- id & nonce2) const;
+ rsa_oaep_sha_data & hmac_key_encrypted) const;
void write_anonymous_cmd(protocol_role role,
std::string const & pattern,
- id const & nonce2);
+ rsa_oaep_sha_data const & hmac_key_encrypted);
void read_auth_cmd(protocol_role & role,
std::string & pattern,
id & client,
id & nonce1,
- id & nonce2,
+ rsa_oaep_sha_data & hmac_key_encrypted,
std::string & signature) const;
void write_auth_cmd(protocol_role role,
std::string const & pattern,
id const & client,
id const & nonce1,
- id const & nonce2,
+ rsa_oaep_sha_data const & hmac_key_encrypted,
std::string const & signature);
- void read_confirm_cmd(std::string & signature) const;
- void write_confirm_cmd(std::string const & signature);
+ void read_confirm_cmd() const;
+ void write_confirm_cmd();
void read_refine_cmd(merkle_node & node) const;
void write_refine_cmd(merkle_node const & node);
============================================================
--- netsync.cc 218fabbe4a06f50089a50833c72f9c1b4ed78a91
+++ netsync.cc cba71cd069c2658e79568849fe0328c92695547d
@@ -112,18 +112,34 @@
// protocol
// --------
//
+// The protocol is a simple binary command-packet system over TCP;
+// each packet consists of a single byte which identifies the protocol
+// version, a byte which identifies the command name inside that
+// version, a size_t sent as a uleb128 indicating the length of the
+// packet, that many bytes of payload, and finally 20 bytes of SHA-1
+// HMAC calculated over the payload. The key for the SHA-1 HMAC is 20
+// bytes of 0 during authentication, and a 20-byte random key chosen
+// by the client after authentication (discussed below).
+//
+//---- Pre-v5 packet format ----
+//
// the protocol is a simple binary command-packet system over tcp; each
// packet consists of a byte which identifies the protocol version, a byte
// which identifies the command name inside that version, a size_t sent as
// a uleb128 indicating the length of the packet, and then that many bytes
// of payload, and finally 4 bytes of adler32 checksum (in LSB order) over
-// the payload. decoding involves simply buffering until a sufficient
-// number of bytes are received, then advancing the buffer pointer. any
-// time an adler32 check fails, the protocol is assumed to have lost
-// synchronization, and the connection is dropped. the parties are free to
-// drop the tcp stream at any point, if too much data is received or too
-// much idle time passes; no commitments or transactions are made.
+// the payload.
//
+// ---- end pre-v5 packet format ----
+//
+// decoding involves simply buffering until a sufficient number of
+// bytes are received, then advancing the buffer pointer. any time an
+// integrity check (adler32 for pre-v5, HMAC for post-v5) fails, the
+// protocol is assumed to have lost synchronization, and the
+// connection is dropped. the parties are free to drop the tcp stream
+// at any point, if too much data is received or too much idle time
+// passes; no commitments or transactions are made.
+//
// one special command, "bye", is used to shut down a connection
// gracefully. once each side has received all the data they want, they
// can send a "bye" command to the other side. as soon as either side has
@@ -135,6 +151,31 @@
// "hello <id> <nonce>" command, which identifies the server's RSA key and
// issues a nonce which must be used for a subsequent authentication.
//
+// The client then responds with either:
+//
+// An "auth (source|sink|both) <pattern> <id> <nonce1> <hmac key>
+// <sig>" command, which identifies its RSA key, notes the role it
+// wishes to play in the synchronization, identifies the pattern it
+// wishes to sync with, signs the previous nonce with its own key, and
+// informs the server of the HMAC key it wishes to use for this
+// session (encrypted with the server's public key); or
+//
+// An "anonymous (source|sink|both) <pattern> <hmac key>" command,
+// which identifies the role it wishes to play in the synchronization,
+// the pattern it ishes to sync with, and the HMAC key it wishes to
+// use for this session (also encrypted with the server's public key).
+//
+// The server then replies with a "confirm" command, which contains no
+// other data but will only have the correct HMAC integrity code if
+// the server received and properly decrypted the HMAC key offered by
+// the client. This transitions the peers into an authenticated state
+// and begins refinement.
+//
+// ---- Pre-v5 authentication process notes ----
+//
+// the exchange begins in a non-authenticated state. the server sends a
+// "hello <id> <nonce>" command, which identifies the server's RSA key and
+// issues a nonce which must be used for a subsequent authentication.
// the client can then respond with an "auth (source|sink|both)
// <pattern> <id> <nonce1> <nonce2> <sig>" command which identifies its
// RSA key, notes the role it wishes to play in the synchronization,
@@ -146,6 +187,8 @@
// the signature of the second nonce sent by the client. this
// transitions the peers into an authenticated state and begins refinement.
//
+// ---- End pre-v5 authentication process ----
+//
// refinement begins with the client sending its root public key and
// manifest certificate merkle nodes to the server. the server then
// compares the root to each slot in *its* root node, and for each slot
@@ -221,7 +264,6 @@ session
string outbuf;
netcmd cmd;
- u8 protocol_version;
bool armed;
bool arm();
@@ -229,6 +271,9 @@ session
boost::regex pattern_re;
id remote_peer_key_hash;
rsa_keypair_id remote_peer_key_name;
+ netsync_session_key session_key;
+ netsync_hmac_value read_hmac;
+ netsync_hmac_value write_hmac;
bool authenticated;
time_t last_io_time;
@@ -321,14 +366,16 @@ session
id const & nonce);
void queue_anonymous_cmd(protocol_role role,
string const & pattern,
- id const & nonce2);
+ id const & nonce2,
+ base64<rsa_pub_key> server_key_encoded);
void queue_auth_cmd(protocol_role role,
string const & pattern,
id const & client,
id const & nonce1,
id const & nonce2,
- string const & signature);
- void queue_confirm_cmd(string const & signature);
+ string const & signature,
+ base64<rsa_pub_key> server_key_encoded);
+ void queue_confirm_cmd();
void queue_refine_cmd(merkle_node const & node);
void queue_send_data_cmd(netcmd_item_type type,
id const & item);
@@ -352,15 +399,15 @@ session
rsa_pub_key const & server_key,
id const & nonce);
bool process_anonymous_cmd(protocol_role role,
- string const & pattern,
- id const & nonce2);
+ string const & pattern);
bool process_auth_cmd(protocol_role role,
string const & pattern,
id const & client,
id const & nonce1,
- id const & nonce2,
string const & signature);
+ void respond_to_auth_cmd(rsa_oaep_sha_data hmac_key_encrypted);
bool process_confirm_cmd(string const & signature);
+ void respond_to_confirm_cmd();
bool process_refine_cmd(merkle_node const & node);
bool process_send_data_cmd(netcmd_item_type type,
id const & item);
@@ -433,12 +480,14 @@ session::session(protocol_role role,
str(sock, to),
inbuf(""),
outbuf(""),
- protocol_version(constants::netcmd_current_protocol_version),
armed(false),
pattern(""),
pattern_re(".*"),
remote_peer_key_hash(""),
remote_peer_key_name(""),
+ session_key(constants::netsync_key_initializer),
+ read_hmac(constants::netsync_key_initializer),
+ write_hmac(constants::netsync_key_initializer),
authenticated(false),
last_io_time(::time(NULL)),
byte_in_ticker(NULL),
@@ -729,7 +778,7 @@ session::write_netcmd_and_try_flush(netc
session::write_netcmd_and_try_flush(netcmd const & cmd)
{
if (!encountered_error)
- cmd.write(outbuf);
+ cmd.write(outbuf, session_key, write_hmac);
else
L(F("dropping outgoing netcmd (because we're in error unwind mode)\n"));
// FIXME: this helps keep the protocol pipeline full but it seems to
@@ -1320,7 +1369,7 @@ session::queue_bye_cmd()
session::queue_bye_cmd()
{
L(F("queueing 'bye' command\n"));
- netcmd cmd(protocol_version);
+ netcmd cmd;
cmd.write_bye_cmd();
write_netcmd_and_try_flush(cmd);
this->sent_goodbye = true;
@@ -1330,7 +1379,7 @@ session::queue_error_cmd(string const &
session::queue_error_cmd(string const & errmsg)
{
L(F("queueing 'error' command\n"));
- netcmd cmd(protocol_version);
+ netcmd cmd;
cmd.write_error_cmd(errmsg);
write_netcmd_and_try_flush(cmd);
this->sent_goodbye = true;
@@ -1343,7 +1392,7 @@ session::queue_done_cmd(size_t level,
string typestr;
netcmd_item_type_to_string(type, typestr);
L(F("queueing 'done' command for %s level %s\n") % typestr % level);
- netcmd cmd(protocol_version);
+ netcmd cmd;
cmd.write_done_cmd(level, type);
write_netcmd_and_try_flush(cmd);
}
@@ -1352,7 +1401,7 @@ session::queue_hello_cmd(id const & serv
session::queue_hello_cmd(id const & server,
id const & nonce)
{
- netcmd cmd(protocol_version);
+ netcmd cmd;
hexenc<id> server_encoded;
encode_hexenc(server, server_encoded);
@@ -1369,11 +1418,16 @@ session::queue_anonymous_cmd(protocol_ro
void
session::queue_anonymous_cmd(protocol_role role,
string const & pattern,
- id const & nonce2)
+ id const & nonce2,
+ base64<rsa_pub_key> server_key_encoded)
{
- netcmd cmd(protocol_version);
- cmd.write_anonymous_cmd(role, pattern, nonce2);
+ netcmd cmd;
+ rsa_oaep_sha_data hmac_key_encrypted;
+ encrypt_rsa(app.lua, remote_peer_key_name, server_key_encoded,
+ nonce2(), hmac_key_encrypted);
+ cmd.write_anonymous_cmd(role, pattern, hmac_key_encrypted);
write_netcmd_and_try_flush(cmd);
+ session_key = netsync_session_key(nonce2());
}
void
@@ -1382,18 +1436,23 @@ session::queue_auth_cmd(protocol_role ro
id const & client,
id const & nonce1,
id const & nonce2,
- string const & signature)
+ string const & signature,
+ base64<rsa_pub_key> server_key_encoded)
{
- netcmd cmd(protocol_version);
- cmd.write_auth_cmd(role, pattern, client, nonce1, nonce2, signature);
+ netcmd cmd;
+ rsa_oaep_sha_data hmac_key_encrypted;
+ encrypt_rsa(app.lua, remote_peer_key_name, server_key_encoded,
+ nonce2(), hmac_key_encrypted);
+ cmd.write_auth_cmd(role, pattern, client, nonce1, hmac_key_encrypted, signature);
write_netcmd_and_try_flush(cmd);
+ session_key = netsync_session_key(nonce2());
}
-void
-session::queue_confirm_cmd(string const & signature)
+void
+session::queue_confirm_cmd()
{
- netcmd cmd(protocol_version);
- cmd.write_confirm_cmd(signature);
+ netcmd cmd;
+ cmd.write_confirm_cmd();
write_netcmd_and_try_flush(cmd);
}
@@ -1406,7 +1465,7 @@ session::queue_refine_cmd(merkle_node co
netcmd_item_type_to_string(node.type, typestr);
L(F("queueing request for refinement of %s node '%s', level %d\n")
% typestr % hpref % static_cast<int>(node.level));
- netcmd cmd(protocol_version);
+ netcmd cmd;
cmd.write_refine_cmd(node);
write_netcmd_and_try_flush(cmd);
}
@@ -1436,7 +1495,7 @@ session::queue_send_data_cmd(netcmd_item
L(F("queueing request for data of %s item '%s'\n")
% typestr % hid);
- netcmd cmd(protocol_version);
+ netcmd cmd;
cmd.write_send_data_cmd(type, item);
write_netcmd_and_try_flush(cmd);
note_item_requested(type, item);
@@ -1472,7 +1531,7 @@ session::queue_send_delta_cmd(netcmd_ite
L(F("queueing request for contents of %s delta '%s' -> '%s'\n")
% typestr % base_hid % ident_hid);
- netcmd cmd(protocol_version);
+ netcmd cmd;
cmd.write_send_delta_cmd(type, base, ident);
write_netcmd_and_try_flush(cmd);
note_item_requested(type, ident);
@@ -1497,7 +1556,7 @@ session::queue_data_cmd(netcmd_item_type
L(F("queueing %d bytes of data for %s item '%s'\n")
% dat.size() % typestr % hid);
- netcmd cmd(protocol_version);
+ netcmd cmd;
cmd.write_data_cmd(type, item, dat);
write_netcmd_and_try_flush(cmd);
note_item_sent(type, item);
@@ -1527,7 +1586,7 @@ session::queue_delta_cmd(netcmd_item_typ
L(F("queueing %s delta '%s' -> '%s'\n")
% typestr % base_hid % ident_hid);
- netcmd cmd(protocol_version);
+ netcmd cmd;
cmd.write_delta_cmd(type, base, ident, del);
write_netcmd_and_try_flush(cmd);
note_item_sent(type, ident);
@@ -1550,7 +1609,7 @@ session::queue_nonexistant_cmd(netcmd_it
L(F("queueing note of nonexistance of %s item '%s'\n")
% typestr % hid);
- netcmd cmd(protocol_version);
+ netcmd cmd;
cmd.write_nonexistant_cmd(type, item);
write_netcmd_and_try_flush(cmd);
}
@@ -1636,22 +1695,6 @@ get_branches(app_state & app, vector<str
W(F("No branches found."));
}
-void
-convert_pattern(utf8 & pat, utf8 & conv)
-{
- string x = pat();
- string pattern = "";
- string e = ".|*?+()[]{}^$\\";
- for (string::const_iterator i = x.begin(); i != x.end(); i++)
- {
- if (e.find(*i) != e.npos)
- pattern += '\\';
- pattern += *i;
- }
- conv = pattern + ".*";
-}
-
-
static const var_domain known_servers_domain = var_domain("known-servers");
bool
@@ -1716,13 +1759,6 @@ session::process_hello_cmd(rsa_keypair_i
}
utf8 pat(pattern);
- if (protocol_version < 5)
- {
- W(F("Talking to an old server. "
- "Using %s as a collection, not a regex.") % pattern);
- convert_pattern(pattern, pat);
- this->pattern_re = boost::regex(pat());
- }
vector<string> branchnames;
set<utf8> ok_branches;
get_branches(app, branchnames);
@@ -1756,11 +1792,12 @@ session::process_hello_cmd(rsa_keypair_i
// make a new nonce of our own and send off the 'auth'
queue_auth_cmd(this->role, this->pattern(), our_key_hash_raw,
- nonce, mk_nonce(), sig_raw());
+ nonce, mk_nonce(), sig_raw(), their_key_encoded);
}
else
{
- queue_anonymous_cmd(this->role, this->pattern(), mk_nonce());
+ queue_anonymous_cmd(this->role, this->pattern(),
+ mk_nonce(), their_key_encoded);
}
return true;
}
@@ -1778,18 +1815,8 @@ session::process_anonymous_cmd(protocol_
bool
session::process_anonymous_cmd(protocol_role role,
- string const & pattern,
- id const & nonce2)
+ string const & pattern)
{
- hexenc<id> hnonce2;
- encode_hexenc(nonce2, hnonce2);
-
- L(F("received 'anonymous' netcmd from client for pattern '%s' "
- "in %s mode with nonce2 '%s'\n")
- % pattern % (role == source_and_sink_role ? "source and sink" :
- (role == source_role ? "source " : "sink"))
- % hnonce2);
-
//
// internally netsync thinks in terms of sources and sinks. users like
// thinking of repositories as "readonly", "readwrite", or "writeonly".
@@ -1850,15 +1877,6 @@ session::process_anonymous_cmd(protocol_
rebuild_merkle_trees(app, ok_branches);
- // get our private key and sign back
- L(F("anonymous read permitted, signing back nonce\n"));
- base64<rsa_sha1_signature> sig;
- rsa_sha1_signature sig_raw;
- base64< arc4<rsa_priv_key> > our_priv;
- load_priv_key(app, app.signing_key, our_priv);
- make_signature(app.lua, app.signing_key, our_priv, nonce2(), sig);
- decode_base64(sig, sig_raw);
- queue_confirm_cmd(sig_raw());
this->pattern = pattern;
this->remote_peer_key_name = rsa_keypair_id("");
this->authenticated = true;
@@ -1866,20 +1884,16 @@ session::process_anonymous_cmd(protocol_
return true;
}
-bool
-session::process_auth_cmd(protocol_role role,
- string const & pattern,
- id const & client,
- id const & nonce1,
- id const & nonce2,
+bool
+session::process_auth_cmd(protocol_role role,
+ string const & pattern,
+ id const & client,
+ id const & nonce1,
string const & signature)
{
I(this->remote_peer_key_hash().size() == 0);
I(this->saved_nonce().size() == constants::merkle_hash_length_in_bytes);
- hexenc<id> hnonce1, hnonce2;
- encode_hexenc(nonce1, hnonce1);
- encode_hexenc(nonce2, hnonce2);
hexenc<id> their_key_hash;
encode_hexenc(client, their_key_hash);
set<utf8> ok_branches;
@@ -1893,12 +1907,6 @@ session::process_auth_cmd(protocol_role
}
boost::regex reg(pattern);
- L(F("received 'auth' netcmd from client '%s' for pattern '%s' "
- "in %s mode with nonce1 '%s' and nonce2 '%s'\n")
- % their_key_hash % pattern % (role == source_and_sink_role ? "source and sink" :
- (role == source_role ? "source " : "sink"))
- % hnonce1 % hnonce2);
-
// check that they replied with the nonce we asked for
if (!(nonce1 == this->saved_nonce))
{
@@ -2004,13 +2012,6 @@ session::process_auth_cmd(protocol_role
{
// get our private key and sign back
L(F("client signature OK, accepting authentication\n"));
- base64<rsa_sha1_signature> sig;
- rsa_sha1_signature sig_raw;
- base64< arc4<rsa_priv_key> > our_priv;
- load_priv_key(app, app.signing_key, our_priv);
- make_signature(app.lua, app.signing_key, our_priv, nonce2(), sig);
- decode_base64(sig, sig_raw);
- queue_confirm_cmd(sig_raw());
this->pattern = pattern;
this->pattern_re = boost::regex(this->pattern());
this->authenticated = true;
@@ -2039,6 +2040,18 @@ session::process_auth_cmd(protocol_role
return false;
}
+void
+session::respond_to_auth_cmd(rsa_oaep_sha_data hmac_key_encrypted)
+{
+ L(F("Writing HMAC confirm command"));
+ base64< arc4<rsa_priv_key> > our_priv;
+ load_priv_key(app, app.signing_key, our_priv);
+ string hmac_key;
+ decrypt_rsa(app.lua, app.signing_key, our_priv, hmac_key_encrypted, hmac_key);
+ session_key = netsync_session_key(hmac_key);
+ queue_confirm_cmd();
+}
+
bool
session::process_confirm_cmd(string const & signature)
{
@@ -2065,20 +2078,6 @@ session::process_confirm_cmd(string cons
if (check_signature(app.lua, their_id, their_key, this->saved_nonce(), sig))
{
L(F("server signature OK, accepting authentication\n"));
- this->authenticated = true;
-
- merkle_ptr root;
- load_merkle_node(epoch_item, 0, get_root_prefix().val, root);
- queue_refine_cmd(*root);
- queue_done_cmd(0, epoch_item);
-
- load_merkle_node(key_item, 0, get_root_prefix().val, root);
- queue_refine_cmd(*root);
- queue_done_cmd(0, key_item);
-
- load_merkle_node(cert_item, 0, get_root_prefix().val, root);
- queue_refine_cmd(*root);
- queue_done_cmd(0, cert_item);
return true;
}
else
@@ -2093,6 +2092,23 @@ session::process_confirm_cmd(string cons
return false;
}
+void
+session::respond_to_confirm_cmd()
+{
+ merkle_ptr root;
+ load_merkle_node(epoch_item, 0, get_root_prefix().val, root);
+ queue_refine_cmd(*root);
+ queue_done_cmd(0, epoch_item);
+
+ load_merkle_node(key_item, 0, get_root_prefix().val, root);
+ queue_refine_cmd(*root);
+ queue_done_cmd(0, key_item);
+
+ load_merkle_node(cert_item, 0, get_root_prefix().val, root);
+ queue_refine_cmd(*root);
+ queue_done_cmd(0, cert_item);
+}
+
static bool
data_exists(netcmd_item_type type,
id const & item,
@@ -2985,8 +3001,6 @@ session::dispatch_payload(netcmd const &
rsa_pub_key server_key;
id nonce;
cmd.read_hello_cmd(server_keyname, server_key, nonce);
- if (cmd.get_version() < protocol_version)
- protocol_version = cmd.get_version();
return process_hello_cmd(server_keyname, server_key, nonce);
}
break;
@@ -3000,11 +3014,17 @@ session::dispatch_payload(netcmd const &
{
protocol_role role;
string pattern;
- id nonce2;
- cmd.read_anonymous_cmd(role, pattern, nonce2);
- if (cmd.get_version() < protocol_version)
- protocol_version = cmd.get_version();
- return process_anonymous_cmd(role, pattern, nonce2);
+ rsa_oaep_sha_data hmac_key_encrypted;
+ cmd.read_anonymous_cmd(role, pattern, hmac_key_encrypted);
+ L(F("received 'anonymous' netcmd from client for pattern '%s' "
+ "in %s mode\n")
+ % pattern % (role == source_and_sink_role ? "source and sink" :
+ (role == source_role ? "source " : "sink")));
+
+ if (!process_anonymous_cmd(role, pattern))
+ return false;
+ respond_to_auth_cmd(hmac_key_encrypted);
+ return true;
}
break;
@@ -3015,11 +3035,25 @@ session::dispatch_payload(netcmd const &
protocol_role role;
string pattern, signature;
id client, nonce1, nonce2;
- cmd.read_auth_cmd(role, pattern, client, nonce1, nonce2, signature);
- if (cmd.get_version() < protocol_version)
- protocol_version = cmd.get_version();
- return process_auth_cmd(role, pattern, client,
- nonce1, nonce2, signature);
+ rsa_oaep_sha_data hmac_key_encrypted;
+ cmd.read_auth_cmd(role, pattern, client, nonce1,
+ hmac_key_encrypted, signature);
+
+ hexenc<id> their_key_hash;
+ encode_hexenc(client, their_key_hash);
+ hexenc<id> hnonce1;
+ encode_hexenc(nonce1, hnonce1);
+
+ L(F("received 'auth(hmac)' netcmd from client '%s' for pattern '%s' "
+ "in %s mode with nonce1 '%s'\n")
+ % their_key_hash % pattern % (role == source_and_sink_role ? "source and sink" :
+ (role == source_role ? "source " : "sink"))
+ % hnonce1);
+
+ if (!process_auth_cmd(role, pattern, client, nonce1, signature))
+ return false;
+ respond_to_auth_cmd(hmac_key_encrypted);
+ return true;
}
break;
@@ -3028,8 +3062,10 @@ session::dispatch_payload(netcmd const &
require(voice == client_voice, "confirm netcmd received in client voice");
{
string signature;
- cmd.read_confirm_cmd(signature);
- return process_confirm_cmd(signature);
+ cmd.read_confirm_cmd();
+ this->authenticated = true;
+ respond_to_confirm_cmd();
+ return true;
}
break;
@@ -3151,7 +3187,7 @@ session::arm()
{
if (!armed)
{
- if (cmd.read(inbuf))
+ if (cmd.read(inbuf, session_key, read_hmac))
{
// inbuf.erase(0, cmd.encoded_size());
armed = true;
@@ -3179,7 +3215,7 @@ bool session::process()
}
catch (bad_decode & bd)
{
- W(F("caught bad_decode exception processing peer %s: '%s'\n") % peer_id % bd.what);
+ W(F("protocol error while processing peer %s: '%s'\n") % peer_id % bd.what);
return false;
}
}
@@ -3212,7 +3248,7 @@ call_server(protocol_role role,
}
catch (bad_decode & bd)
{
- W(F("caught bad_decode exception decoding input from peer %s: '%s'\n")
+ W(F("protocol error while processing peer %s: '%s'\n")
% sess.peer_id % bd.what);
return;
}
@@ -3239,7 +3275,7 @@ call_server(protocol_role role,
}
catch (bad_decode & bd)
{
- W(F("caught bad_decode exception decoding input from peer %s: '%s'\n")
+ W(F("protocol error while processing peer %s: '%s'\n")
% sess.peer_id % bd.what);
return;
}
@@ -3313,7 +3349,7 @@ arm_sessions_and_calculate_probe(Netxx::
}
catch (bad_decode & bd)
{
- W(F("caught bad_decode exception decoding input from peer %s: '%s', marking as bad\n")
+ W(F("protocol error while processing peer %s: '%s', marking as bad\n")
% i->second->peer_id % bd.what);
arm_failed.insert(i->first);
}
@@ -3370,7 +3406,7 @@ handle_read_available(Netxx::socket_type
}
catch (bad_decode & bd)
{
- W(F("caught bad_decode exception decoding input from peer %s: '%s', disconnecting\n")
+ W(F("protocol error while processing peer %s: '%s', disconnecting\n")
% sess->peer_id % bd.what);
sessions.erase(fd);
live_p = false;
@@ -3616,21 +3652,6 @@ session::rebuild_merkle_trees(app_state
ticker certs_ticker("certs", "c", 256);
ticker keys_ticker("keys", "k", 1);
- // this code is wrong. the way the logic _should_ work is:
- // -- start with all branches we want to include
- // -- for each such branch, find all branch certs for that branch
- // -- for each such cert, note down its revision
- // (or these two steps can be replaced with anything else that gives us
- // list of all revisions in the branch)
- // -- expand this set of revisions to include all of their ancestors
- // -- for each such revision, insert all of its certs into the cert table,
- // and note all of its branches and keys
- // -- for each such branch, insert its epoch into the epoch table, and for
- // each such key, insert its key into the key table.
- // this somewhat convoluted approach is necessary to handle cases where
- // ancestors leave the branch inclusion set, where revisions carry branches
- // that are otherwise outside of the inclusion set, etc.
-
set<revision_id> revision_ids;
set<rsa_keypair_id> inserted_keys;
@@ -3649,6 +3670,9 @@ session::rebuild_merkle_trees(app_state
}
}
+ // FIXME: we should probably include epochs for all branches mentioned in
+ // any included branch cert, rather than just for branches included by the
+ // branch mask
{
map<cert_value, epoch_data> epochs;
app.db.get_epochs(epochs);
============================================================
--- std_hooks.lua c06a28443baa42ca4d4f349a6dd9653d25fa8c0f
+++ std_hooks.lua 9b1a27e30129929aa366223e51f1c11ec918fc93
@@ -86,6 +86,11 @@ function ignore_file(name)
if (string.find(name, "/autom4te.cache/")) then return true end
if (string.find(name, "^.deps/")) then return true end
if (string.find(name, "/.deps/")) then return true end
+ -- Cons/SCons detritus:
+ if (string.find(name, "^.consign$")) then return true end
+ if (string.find(name, "/.consign$")) then return true end
+ if (string.find(name, "^.sconsign$")) then return true end
+ if (string.find(name, "/.sconsign$")) then return true end
-- other VCSes:
if (string.find(name, "^CVS/")) then return true end
if (string.find(name, "/CVS/")) then return true end
============================================================
--- tests/t_cat_file_by_name.at 009033538c4424ad6994c33eef9dab24bef15980
+++ tests/t_cat_file_by_name.at 45f9704b56b2b74f16c5579539bc1c6f777b1e56
@@ -7,10 +7,14 @@ AT_DATA(r1testfile, [r1 test file
])
AT_DATA(r1testfile, [r1 test file
])
+AT_DATA(subfile, [data in subfile
+])
AT_CHECK(cp r0testfile testfile)
AT_CHECK(cp r0otherfile otherfile)
-AT_CHECK(MONOTONE add testfile otherfile, [], [ignore], [ignore])
+AT_CHECK(mkdir subdir)
+AT_CHECK(cp subfile subdir/testfile)
+AT_CHECK(MONOTONE add testfile otherfile subdir/testfile, [], [ignore], [ignore])
COMMIT(testbranch)
R0=`BASE_REVISION`
@@ -30,6 +34,12 @@ AT_CHECK(cmp stdout r1testfile, [], [ign
AT_CHECK(CANONICALISE(stdout))
AT_CHECK(cmp stdout r1testfile, [], [ignore])
+CHECK_SAME_CANONICALISED_STDOUT(cd subdir && MONOTONE cat file $R0 testfile, cat subfile)
+
+AT_CHECK(rm -rf MT)
+
+CHECK_SAME_CANONICALISED_STDOUT(MONOTONE cat file $R0 testfile, cat r0testfile)
+
AT_CHECK(MONOTONE cat file $R0 no_such_file, [1], [ignore], [ignore])
AT_CHECK(MONOTONE cat file $R0 "", [1], [ignore], [ignore])
============================================================
--- tests/t_netsync_diffbranch.at a529b5b568039685d66db00c8550b817fbc3a4a4
+++ tests/t_netsync_diffbranch.at 6a2a4e9b6dada24dea2ad88bd0c3d0caf8d180bc
@@ -1,4 +1,4 @@
-AT_SETUP([(normal) pull a netsync branch which has a parent from another branch])
+AT_SETUP([pull a netsync branch which has a parent from another branch])
AT_KEYWORDS([netsync])
============================================================
--- testsuite.at 2635480ab5d48f5eae96909a5e59ef20a7b0c36d
+++ testsuite.at 2e17e62213b9878d08d7c792f244b6e193498618
@@ -659,4 +659,5 @@ m4_include(tests/t_status.at)
m4_include(tests/t_merge_manual.at)
m4_include(tests/t_revert_restrict.at)
m4_include(tests/t_status.at)
+m4_include(tests/t_rcfile_dir.at)
+m4_include(tests/t_lua_includedir.at)
-
============================================================
--- vocab.cc 6e07baffa263c80f013d474ad362c3b52c162201
+++ vocab.cc 0a568b1d7a988e146671055888136fdff7818916
@@ -102,7 +102,43 @@ verify(rsa_keypair_id & val)
val.ok = true;
}
+inline void
+verify(netsync_session_key & val)
+{
+ if (val.ok)
+ return;
+ if (val().size() == 0)
+ {
+ val.s.append(constants::netsync_session_key_length_in_bytes, 0);
+ return;
+ }
+
+ N(val().size() == constants::netsync_session_key_length_in_bytes,
+ F("Invalid key length of %d bytes") % val().length());
+
+ val.ok = true;
+}
+
+inline void
+verify(netsync_hmac_value & val)
+{
+ if (val.ok)
+ return;
+
+ if (val().size() == 0)
+ {
+ val.s.append(constants::netsync_hmac_value_length_in_bytes, 0);
+ return;
+ }
+
+ N(val().size() == constants::netsync_hmac_value_length_in_bytes,
+ F("Invalid key length of %d bytes") % val().length());
+
+ val.ok = true;
+}
+
+
inline void
verify(local_path & val)
{
============================================================
--- vocab_terms.hh c12dd87ed775d3e2d8713a393cc055ce68e5ae16
+++ vocab_terms.hh fe3bf7a7700bdde82211ba0be39caf248025e00b
@@ -34,7 +34,11 @@ ATOMIC_NOVERIFY(rsa_sha1_signature); //
ATOMIC_NOVERIFY(rsa_pub_key); // some nice numbers
ATOMIC_NOVERIFY(rsa_priv_key); // some nice numbers
ATOMIC_NOVERIFY(rsa_sha1_signature); // some other nice numbers
+ATOMIC_NOVERIFY(rsa_oaep_sha_data);
+ATOMIC(netsync_session_key); // key for netsync session HMAC
+ATOMIC(netsync_hmac_value); // 160-bit SHA-1 HMAC
+
DECORATE(revision); // thing associated with a revision
DECORATE(manifest); // thing associated with a manifest
DECORATE(file); // thing associated with a file