/** * Callback to display an error message when XML validation fails. * * @param ctx * XML context. * @param format * Format specifier. * @param ... * Variable arguments. */ static void parse_metaserver_data_error (void *ctx, const char *format, ...) { va_list args; va_start(args, format); char formatted[HUGE_BUF * 32]; vsnprintf(VS(formatted), format, args); string_strip_newline(formatted); va_end(args); LOG(ERROR, "%s", formatted); }
extern const unsigned char *file_get_line(void) { xString* const line = __file_get_line(); const unsigned char* result = NULL; if (line != NULL) { result = (const unsigned char*) get_string_value(line); string_strip_newline(line); } return result; }
/** * Parse the metaserver certificate information. * * @param server * Metaserver entry. * @return * True on success, false on failure. */ static bool parse_metaserver_cert (server_struct *server) { HARD_ASSERT(server != NULL); if (server->cert == NULL || server->cert_sig == NULL) { /* No certificate. */ return true; } /* Generate a SHA512 hash of the certificate's contents. */ unsigned char cert_digest[SHA512_DIGEST_LENGTH]; if (SHA512((unsigned char *) server->cert, strlen(server->cert), cert_digest) == NULL) { LOG(ERROR, "SHA512() failed: %s", ERR_error_string(ERR_get_error(), NULL)); return false; } char cert_hash[SHA512_DIGEST_LENGTH * 2 + 1]; SOFT_ASSERT_RC(string_tohex(VS(cert_digest), VS(cert_hash), false) == sizeof(cert_hash) - 1, false, "string_tohex failed"); string_tolower(cert_hash); /* Verify the signature. */ if (!curl_verify(CURL_PKEY_TRUST_ULTIMATE, cert_hash, strlen(cert_hash), server->cert_sig, server->cert_sig_len)) { LOG(ERROR, "Failed to verify signature"); return false; } server_cert_info_t *info = ecalloc(1, sizeof(*info)); char buf[MAX_BUF]; size_t pos = 0; bool in_info = false; while (string_get_word(server->cert, &pos, '\n', VS(buf), 0)) { char *cp = buf; string_skip_whitespace(cp); string_strip_newline(cp); if (*cp == '\0') { continue; } if (strcmp(cp, cert_begin_str) == 0) { in_info = true; continue; } else if (!in_info) { continue; } else if (strcmp(cp, cert_end_str) == 0) { break; } char *cps[2]; if (string_split(cp, cps, arraysize(cps), ':') != arraysize(cps)) { LOG(ERROR, "Parsing error"); continue; } string_tolower(cps[0]); string_skip_whitespace(cps[1]); const char *key = cps[0]; const char *value = cps[1]; char **content = NULL; if (strcmp(key, "name") == 0) { content = &info->name; } else if (strcmp(key, "hostname") == 0) { content = &info->hostname; } else if (strcmp(key, "ipv4 address") == 0) { content = &info->ipv4_address; } else if (strcmp(key, "ipv6 address") == 0) { content = &info->ipv6_address; } else if (strcmp(key, "public key") == 0) { content = &info->pubkey; } else if (strcmp(key, "port") == 0) { info->port = atoi(value); } else if (strcmp(key, "crypto port") == 0) { info->port_crypto = atoi(value); } else { LOG(DEVEL, "Unrecognized key: %s", key); continue; } if (content != NULL) { StringBuffer *sb = stringbuffer_new(); if (*content != NULL) { stringbuffer_append_string(sb, *content); stringbuffer_append_char(sb, '\n'); efree(*content); } stringbuffer_append_string(sb, value); *content = stringbuffer_finish(sb); } } /* Ensure we got the data we need. */ if (info->name == NULL || info->hostname == NULL || info->pubkey == NULL || info->port_crypto <= 0 || (info->ipv4_address == NULL) != (info->ipv6_address == NULL)) { LOG(ERROR, "Certificate is missing required data."); goto error; } /* Ensure certificate attributes match the advertised ones. */ if (strcmp(info->hostname, server->hostname) != 0) { LOG(ERROR, "Certificate hostname does not match advertised hostname."); goto error; } if (strcmp(info->name, server->name) != 0) { LOG(ERROR, "Certificate name does not match advertised name."); goto error; } if (info->port != server->port) { LOG(ERROR, "Certificate port does not match advertised port."); goto error; } if (info->port_crypto != server->port_crypto) { LOG(ERROR, "Certificate crypto port does not match advertised crypto port."); goto error; } server->cert_info = info; return true; error: metaserver_cert_free(info); return false; }