# # # patch "cmd_netsync.cc" # from [daaee53881abbda9c400148f4870098cb03508de] # to [951e1911bb05335819bd88ec2254abc3a7a6b3e2] # # patch "key_store.cc" # from [459dd9ae128419afaaa65dd6bb7d3d725a32f207] # to [dd421fdba82efcb2ad205f9f7e0b10cec03e19dd] # # patch "key_store.hh" # from [1f5935e3c1fc3dc4063729c43be2c256c00857f0] # to [8e153d25c4eade5bfa89e2d08e655cb04da5f54b] # # patch "keys.cc" # from [95fc131d10d44a59802efd24ade47d2320530036] # to [a111c06577013600b133622cace32e01167a980a] # # patch "keys.hh" # from [6cc9c7d9f206f27bfcef3269694585f0eda7eaa1] # to [3f2b30c5be68a0b7fbb2e5190079bab7a5d96215] # # patch "netsync.cc" # from [9352f59c67d47e16991713015356bc0bc971b863] # to [8aa8a063c4a6a506a7e30d2e1ff85a38da445253] # # patch "project.cc" # from [a56b3892d2c9cdaa9a1de14bd420ecafe273b9a9] # to [b8e1ba0855c681464eb187d0a9fb941405374a27] # # patch "tests/quiet_turns_off_tickers_but_not_warnings/__driver__.lua" # from [581d68e2a1f364bf5c74cefc7a3e8da4cf9fdc81] # to [3058caa495242e00ca406633e868782ad6e8cc42] # ============================================================ --- cmd_netsync.cc daaee53881abbda9c400148f4870098cb03508de +++ cmd_netsync.cc 951e1911bb05335819bd88ec2254abc3a7a6b3e2 @@ -56,22 +56,14 @@ find_key(options & opts, netsync_connection_info const & info, bool need_key = true) { - if (!opts.signing_key().empty()) - return; - - rsa_keypair_id key; - utf8 host(info.client.unparsed); if (!info.client.u.host.empty()) host = utf8(info.client.u.host, origin::user); - if (!lua.hook_get_netsync_key(host, - info.client.include_pattern, - info.client.exclude_pattern, key) - && need_key) - get_user_key(opts, lua, db, keys, key); - - opts.signing_key = key; + cache_netsync_key(opts, lua, db, keys, host, + info.client.include_pattern, + info.client.exclude_pattern, + need_key ? KEY_REQUIRED : KEY_OPTIONAL); } static void @@ -265,7 +257,7 @@ CMD(pull, "pull", "", CMD_REF(network), extract_client_connection_info(app.opts, app.lua, db, keys, args, info, false); - if (app.opts.signing_key() == "") + if (!keys.have_signing_key()) P(F("doing anonymous pull; use -kKEYNAME if you need authentication")); run_netsync_protocol(app.opts, app.lua, project, keys, @@ -396,7 +388,7 @@ CMD(clone, "clone", "", CMD_REF(network) build_client_connection_info(app.opts, app.lua, db, keys, info, true, true, false); - if (app.opts.signing_key() == "") + if (!keys.have_signing_key()) P(F("doing anonymous pull; use -kKEYNAME if you need authentication")); // make sure we're back in the original dir so that file: URIs work ============================================================ --- key_store.cc 459dd9ae128419afaaa65dd6bb7d3d725a32f207 +++ key_store.cc dd421fdba82efcb2ad205f9f7e0b10cec03e19dd @@ -177,6 +177,12 @@ key_store::~key_store() key_store::~key_store() {} +bool +key_store::have_signing_key() const +{ + return !signing_key().empty(); +} + #if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,7,7) Botan::RandomNumberGenerator & key_store::get_rng() ============================================================ --- key_store.hh 1f5935e3c1fc3dc4063729c43be2c256c00857f0 +++ key_store.hh 8e153d25c4eade5bfa89e2d08e655cb04da5f54b @@ -48,6 +48,7 @@ public: public: rsa_keypair_id signing_key; + bool have_signing_key() const; explicit key_store(app_state & a); ~key_store(); ============================================================ --- keys.cc 95fc131d10d44a59802efd24ade47d2320530036 +++ keys.cc a111c06577013600b133622cace32e01167a980a @@ -49,65 +49,134 @@ load_key_pair(key_store & keys, keys.get_key_pair(id, kp); } -// Find the key to be used for signing certs. If possible, ensure the -// database and the key_store agree on that key, and cache it in decrypted -// form, so as not to bother the user for their passphrase later. +namespace { + void check_and_save_chosen_key(database & db, + key_store & keys, + rsa_keypair_id const & chosen_key) + { + // Ensure that the specified key actually exists. + keypair priv_key; + load_key_pair(keys, chosen_key, priv_key); + if (db.database_specified()) + { + // If the database doesn't have this public key, add it now; otherwise + // make sure the database and key-store agree on the public key. + if (!db.public_key_exists(chosen_key)) + db.put_key(chosen_key, priv_key.pub); + else + { + rsa_pub_key pub_key; + db.get_key(chosen_key, pub_key); + E(keys_match(chosen_key, pub_key, chosen_key, priv_key.pub), + origin::no_fault, + F("The key '%s' stored in your database does\n" + "not match the version in your local key store!") + % chosen_key); + } + } + + // Decrypt and cache the key now. + keys.cache_decrypted_key(chosen_key); + } + bool get_only_key(key_store & keys, bool required, rsa_keypair_id & key) + { + vector all_privkeys; + keys.get_key_ids(all_privkeys); + E(!required || !all_privkeys.empty(), origin::user, + F("you have no private key to make signatures with\n" + "perhaps you need to 'genkey '")); + E(!required || all_privkeys.size() < 2, origin::user, + F("you have multiple private keys\n" + "pick one to use for signatures by adding " + "'-k' to your command")); + + if (all_privkeys.size() == 1) + { + key = all_privkeys[0]; + return true; + } + else + { + return false; + } + } +} + void get_user_key(options const & opts, lua_hooks & lua, database & db, key_store & keys, rsa_keypair_id & key) { - if (!keys.signing_key().empty()) + if (keys.have_signing_key()) { key = keys.signing_key; return; } - if (!opts.signing_key().empty()) - key = opts.signing_key; + // key_given is not set if the key option was extracted from the workspace + if (opts.key_given || !opts.signing_key().empty()) + { + if (!opts.signing_key().empty()) + { + key = opts.signing_key; + } + else + { + E(false, origin::user, + F("a key is required for this operation, but the --key option " + "was given with an empty argument")); + } + } else if (lua.hook_get_branch_key(opts.branch, key)) ; // the lua hook sets the key else { - vector all_privkeys; - keys.get_key_ids(all_privkeys); - E(!all_privkeys.empty(), origin::user, - F("you have no private key to make signatures with\n" - "perhaps you need to 'genkey '")); - E(all_privkeys.size() < 2, origin::user, - F("you have multiple private keys\n" - "pick one to use for signatures by adding " - "'-k' to your command")); + get_only_key(keys, true, key); + } - key = all_privkeys[0]; + check_and_save_chosen_key(db, keys, key); +} + +void +cache_netsync_key(options const & opts, lua_hooks & lua, + database & db, key_store & keys, + utf8 const & host, + globish const & include, + globish const & exclude, + netsync_key_requiredness key_requiredness) +{ + if (keys.have_signing_key()) + { + return; } - // Ensure that the specified key actually exists. - keypair priv_key; - load_key_pair(keys, key, priv_key); + bool found_key = false; + rsa_keypair_id key; - if (db.database_specified()) + // key_given is not set if the key option was extracted from the workspace + if (opts.key_given || !opts.signing_key().empty()) { - // If the database doesn't have this public key, add it now; otherwise - // make sure the database and key-store agree on the public key. - if (!db.public_key_exists(key)) - db.put_key(key, priv_key.pub); - else + if (!opts.signing_key().empty()) { - rsa_pub_key pub_key; - db.get_key(key, pub_key); - E(keys_match(key, pub_key, key, priv_key.pub), origin::no_fault, - F("The key '%s' stored in your database does\n" - "not match the version in your local key store!") % key); + key = opts.signing_key; + found_key = true; } } + else if (lua.hook_get_netsync_key(host, include, exclude, key)) + { + found_key = true; + } + else + { + found_key = get_only_key(keys, key_requiredness == KEY_REQUIRED, key); + } - // Decrypt and cache the key now. - keys.cache_decrypted_key(key); + if (found_key) + { + check_and_save_chosen_key(db, keys, key); + } } -// As above, but does not report which key has been selected; for use when -// the important thing is to have selected one and cached the decrypted key. void cache_user_key(options const & opts, lua_hooks & lua, database & db, key_store & keys) ============================================================ --- keys.hh 6cc9c7d9f206f27bfcef3269694585f0eda7eaa1 +++ keys.hh 3f2b30c5be68a0b7fbb2e5190079bab7a5d96215 @@ -17,20 +17,36 @@ struct keypair; class key_store; class database; struct keypair; +class globish; // keys.{hh,cc} does all the "delicate" crypto (meaning: that which needs // to read passphrases and manipulate raw, decrypted private keys). it // could in theory be in transforms.cc too, but that file's already kinda // big and this stuff "feels" different, imho. -// N()'s out if there is no unique key for us to use +// Find the key to be used for signing certs. If possible, ensure the +// database and the key_store agree on that key, and cache it in decrypted +// form, so as not to bother the user for their passphrase later. void get_user_key(options const & opts, lua_hooks & lua, database & db, key_store & keys, rsa_keypair_id & key); +// As above, but does not report which key has been selected; for use when +// the important thing is to have selected one and cached the decrypted key. void cache_user_key(options const & opts, lua_hooks & lua, database & db, key_store & keys); +// Find the key to be used for netsync authentication. If possible, ensure the +// database and the key_store agree on that key, and cache it in decrypted +// form, so as not to bother the user for their passphrase later. +enum netsync_key_requiredness {KEY_OPTIONAL, KEY_REQUIRED}; +void cache_netsync_key(options const & opts, lua_hooks & lua, + database & db, key_store & keys, + utf8 const & host, + globish const & include, + globish const & exclude, + netsync_key_requiredness key_requiredness); + void load_key_pair(key_store & keys, rsa_keypair_id const & id); ============================================================ --- netsync.cc 9352f59c67d47e16991713015356bc0bc971b863 +++ netsync.cc 8aa8a063c4a6a506a7e30d2e1ff85a38da445253 @@ -855,7 +855,7 @@ session::session(options & opts, keys(keys), lua(lua), use_transport_auth(opts.use_transport_auth), - signing_key(opts.signing_key), + signing_key(keys.signing_key), keys_to_push(opts.keys_to_push), armed(false), received_remote_key(false), ============================================================ --- project.cc a56b3892d2c9cdaa9a1de14bd420ecafe273b9a9 +++ project.cc b8e1ba0855c681464eb187d0a9fb941405374a27 @@ -382,7 +382,7 @@ project_t::put_cert(key_store & keys, cert_name const & name, cert_value const & value) { - I(!keys.signing_key().empty()); + I(keys.have_signing_key()); cert t(id, name, value, keys.signing_key); string signed_text; ============================================================ --- tests/quiet_turns_off_tickers_but_not_warnings/__driver__.lua 581d68e2a1f364bf5c74cefc7a3e8da4cf9fdc81 +++ tests/quiet_turns_off_tickers_but_not_warnings/__driver__.lua 3058caa495242e00ca406633e868782ad6e8cc42 @@ -9,7 +9,7 @@ srv = netsync.start() -- check that tickers are quiet srv = netsync.start() -check(mtn2("--rcfile=netsync.lua", "pull", srv.address, "testbranch", "--quiet"), 0, nil, true) +check(mtn2("--rcfile=netsync.lua", "pull", srv.address, "testbranch", "--quiet", "--key="), 0, nil, true) check(qgrep(': warning: ', "stderr")) srv:stop()