-int metakey_h(connection_t *c)
-{
- char buffer[MAX_STRING_SIZE];
- int cipher, digest, maclength, compression;
- int len;
-cp
- if(sscanf(c->buffer, "%*d %d %d %d %d "MAX_STRING, &cipher, &digest, &maclength, &compression, buffer) != 5)
- {
- syslog(LOG_ERR, _("Got bad %s from %s (%s)"), "METAKEY", c->name, c->hostname);
- return -1;
- }
-cp
- len = RSA_size(myself->connection->rsa_key);
-
- /* Check if the length of the meta key is all right */
-
- if(strlen(buffer) != len*2)
- {
- syslog(LOG_ERR, _("Possible intruder %s (%s): %s"), c->name, c->hostname, "wrong keylength");
- return -1;
- }
-
- /* Allocate buffers for the meta key */
-cp
- if(!c->inkey)
- c->inkey = xmalloc(len);
-
- if(!c->inctx)
- c->inctx = xmalloc(sizeof(*c->inctx));
-
- /* Convert the challenge from hexadecimal back to binary */
-cp
- hex2bin(buffer,buffer,len);
-
- /* Decrypt the meta key */
-cp
- if(RSA_private_decrypt(len, buffer, c->inkey, myself->connection->rsa_key, RSA_NO_PADDING) != len) /* See challenge() */
- {
- syslog(LOG_ERR, _("Error during encryption of meta key for %s (%s)"), c->name, c->hostname);
- return -1;
- }
-
- if(debug_lvl >= DEBUG_SCARY_THINGS)
- {
- bin2hex(c->inkey, buffer, len);
- buffer[len*2] = '\0';
- syslog(LOG_DEBUG, _("Received random meta key (unencrypted): %s"), buffer);
- }
-
- /* All incoming requests will now be encrypted. */
-cp
- /* Check and lookup cipher and digest algorithms */
-
- if(cipher)
- {
- c->incipher = EVP_get_cipherbynid(cipher);
- if(!c->incipher)
- {
- syslog(LOG_ERR, _("%s (%s) uses unknown cipher!"), c->name, c->hostname);
- return -1;
- }
-
- EVP_DecryptInit(c->inctx, c->incipher,
- c->inkey + len - c->incipher->key_len,
- c->inkey + len - c->incipher->key_len - c->incipher->iv_len);
-
- c->status.decryptin = 1;
- }
- else
- {
- c->incipher = NULL;
- }
-
- c->inmaclength = maclength;
-
- if(digest)
- {
- c->indigest = EVP_get_digestbynid(digest);
- if(!c->indigest)
- {
- syslog(LOG_ERR, _("Node %s (%s) uses unknown digest!"), c->name, c->hostname);
- return -1;
- }
-
- if(c->inmaclength > c->indigest->md_size || c->inmaclength < 0)
- {
- syslog(LOG_ERR, _("%s (%s) uses bogus MAC length!"), c->name, c->hostname);
- return -1;
- }
- }
- else
- {
- c->indigest = NULL;
- }
-
- c->incompression = compression;
-
- c->allow_request = CHALLENGE;
-cp
- return send_challenge(c);
+bool id_h(connection_t *c, const char *request) {
+ char name[MAX_STRING_SIZE];
+
+ if(sscanf(request, "%*d " MAX_STRING " %d.%d", name, &c->protocol_major, &c->protocol_minor) < 2) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s)", "ID", c->name,
+ c->hostname);
+ return false;
+ }
+
+ /* Check if this is a control connection */
+
+ if(name[0] == '^' && !strcmp(name + 1, controlcookie)) {
+ c->status.control = true;
+ c->allow_request = CONTROL;
+ c->last_ping_time = now.tv_sec + 3600;
+
+ free(c->name);
+ c->name = xstrdup("<control>");
+
+ return send_request(c, "%d %d %d", ACK, TINC_CTL_VERSION_CURRENT, getpid());
+ }
+
+ if(name[0] == '?') {
+ if(!invitation_key) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Got invitation from %s but we don't have an invitation key", c->hostname);
+ return false;
+ }
+
+ c->ecdsa = ecdsa_set_base64_public_key(name + 1);
+ if(!c->ecdsa) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Got bad invitation from %s", c->hostname);
+ return false;
+ }
+
+ c->status.invitation = true;
+ char *mykey = ecdsa_get_base64_public_key(invitation_key);
+ if(!mykey)
+ return false;
+ if(!send_request(c, "%d %s", ACK, mykey))
+ return false;
+ free(mykey);
+
+ c->protocol_minor = 2;
+
+ return sptps_start(&c->sptps, c, false, false, invitation_key, c->ecdsa, "tinc invitation", 15, send_meta_sptps, receive_invitation_sptps);
+ }
+
+ /* Check if identity is a valid name */
+
+ if(!check_id(name)) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s): %s", "ID", c->name,
+ c->hostname, "invalid name");
+ return false;
+ }
+
+ /* If this is an outgoing connection, make sure we are connected to the right host */
+
+ if(c->outgoing) {
+ if(strcmp(c->name, name)) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Peer %s is %s instead of %s", c->hostname, name,
+ c->name);
+ return false;
+ }
+ } else {
+ if(c->name)
+ free(c->name);
+ c->name = xstrdup(name);
+ }
+
+ /* Check if version matches */
+
+ if(c->protocol_major != myself->connection->protocol_major) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Peer %s (%s) uses incompatible version %d.%d",
+ c->name, c->hostname, c->protocol_major, c->protocol_minor);
+ return false;
+ }
+
+ if(bypass_security) {
+ if(!c->config_tree)
+ init_configuration(&c->config_tree);
+ c->allow_request = ACK;
+ return send_ack(c);
+ }
+
+ if(!experimental)
+ c->protocol_minor = 0;
+
+ if(!c->config_tree) {
+ init_configuration(&c->config_tree);
+
+ if(!read_host_config(c->config_tree, c->name)) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Peer %s had unknown identity (%s)", c->hostname, c->name);
+ return false;
+ }
+
+ if(experimental)
+ read_ecdsa_public_key(c);
+ } else {
+ if(c->protocol_minor && !ecdsa_active(c->ecdsa))
+ c->protocol_minor = 1;
+ }
+
+ /* Forbid version rollback for nodes whose ECDSA key we know */
+
+ if(ecdsa_active(c->ecdsa) && c->protocol_minor < 2) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Peer %s (%s) tries to roll back protocol version to %d.%d",
+ c->name, c->hostname, c->protocol_major, c->protocol_minor);
+ return false;
+ }
+
+ c->allow_request = METAKEY;
+
+ if(c->protocol_minor >= 2) {
+ c->allow_request = ACK;
+ char label[25 + strlen(myself->name) + strlen(c->name)];
+
+ if(c->outgoing)
+ snprintf(label, sizeof label, "tinc TCP key expansion %s %s", myself->name, c->name);
+ else
+ snprintf(label, sizeof label, "tinc TCP key expansion %s %s", c->name, myself->name);
+
+ return sptps_start(&c->sptps, c, c->outgoing, false, myself->connection->ecdsa, c->ecdsa, label, sizeof label, send_meta_sptps, receive_meta_sptps);
+ } else {
+ return send_metakey(c);
+ }