/* This function normalizes a path. It simplifies the directory portion and * makes the path absolute if requested. */ void kpath_normalize(kstr *path, int absolute_flag, int format) { struct kpath_dir dir; kstr dir_path, filename, ext; kstr_init(&dir_path); kstr_init(&filename); kstr_init(&ext); kpath_dir_init(&dir); /* If requested, make the path absolute. */ if (absolute_flag) kpath_make_absolute(path, format); /* Split the path. */ kpath_split(path, &dir_path, &filename, &ext, format); /* Decompose, simplify and recompose the directory portion. */ kpath_decompose_dir(&dir_path, &dir, format); kpath_simplify_dir(&dir); kpath_recompose_dir(&dir_path, &dir, format); /* Recompose the path. */ kstr_assign_kstr(path, &dir_path); kstr_append_kstr(path, &filename); if (ext.slen) { kstr_append_char(path, '.'); kstr_append_kstr(path, &ext); } kpath_dir_clean(&dir); kstr_clean(&dir_path); kstr_clean(&filename); kstr_clean(&ext); }
/* This function should be called to log a message in the KMOD log. * Arguments: * Message logging level (1, 2, 3, 4) (higher number is lower priority). * Format is the usual printf() format, and the following args are the args that * printf() takes. */ void kmod_log_msg(int level, const char *format, ...) { va_list arg; char date[256]; kstr str, fmt; time_t now; if ((level & global_opts.log_level) != level) return; va_start(arg, format); kstr_init(&fmt); kstr_init(&str); time(&now); strftime(date, 256, "%Y/%m/%d %H:%M:%S", localtime(&now)); kstr_sf(&fmt, "%s : %s", date, format); kstr_sfv(&str, fmt.data, arg); fprintf(global_opts.log_file, "%s", str.data); va_end(arg); kstr_clean(&str); kstr_clean(&fmt); }
static void test_knp_msg() { kbuffer msg1, msg2; kstr str; uint32_t i32; uint64_t i64; kbuffer_init(&msg1, 0); kbuffer_init(&msg2, 0); kstr_init(&str); knp_msg_write_uint32(&msg1, 3); knp_msg_write_uint64(&msg1, 4); kstr_append_cstr(&str, "hello"); knp_msg_write_kstr(&msg1, &str); kbuffer_write(&msg2, msg1.data, msg1.len); assert(! knp_msg_read_uint32(&msg2, &i32) && i32 == 3); assert(! knp_msg_read_uint64(&msg2, &i64) && i64 == 4); assert(! knp_msg_read_kstr(&msg2, &str) && strcmp(str.data, "hello") == 0); assert(knp_msg_read_uint32(&msg2, &i32)); kbuffer_clean(&msg1); kbuffer_clean(&msg2); kstr_free(&str); }
struct kcd_client* kcd_client_new() { struct kcd_client *self = (struct kcd_client *) kcalloc(sizeof(struct kcd_client)); self->sock = -1; kstr_init(&self->addr); ktls_init(&self->conn); return self; }
static void kcd_vnc_mode_state_init(struct kcd_vnc_mode_state *self, struct kcd_ticket_mode_state *tms) { memset(self, 0, sizeof(struct kcd_vnc_mode_state)); self->proxy_sock = -1; kstr_init(&self->end_error_msg); kcd_process_init(&self->process); self->tms = tms; }
/* This function verifies the integrity of the database. */ int maildb_integrity_check(maildb *mdb) { int error = 0; sqlite3 *db = (sqlite3 *) mdb->db; sqlite3_stmt *stmt = NULL; kstr val; kstr_init(&val); do { if (sqlite3_prepare(db, "PRAGMA integrity_check;", -1, &stmt, NULL) || sqlite3_step(stmt) != SQLITE_ROW) { kmo_seterror(sqlite3_errmsg(db)); error = -1; break; } read_string(stmt, &val, 0); if (strcmp(val.data, "ok")) { kmo_seterror("sqlite database is corrupted"); error = -1; break; } } while (0); kstr_free(&val); finalize_stmt(db, &stmt); return error; }
static void test_kstr() { kstr str1, str2, str3; kstr_init(&str1); kstr_init_cstr(&str2, "bar"); kstr_init_kstr(&str3, &str2); assert(kstr_equal_cstr(&str2, "bar")); assert(kstr_equal_kstr(&str2, &str3)); kstr_assign_cstr(&str3, "foobar"); kstr_assign_kstr(&str1, &str3); kstr_sf(&str2, "%sbar", "foo"); assert(kstr_equal_kstr(&str3, &str2)); assert(kstr_equal_kstr(&str1, &str2)); kstr_sf(&str2, "%s buffer to grow", "I want my"); assert(kstr_equal_cstr(&str2, "I want my buffer to grow")); kstr_clear(&str2); assert(kstr_equal_cstr(&str2, "")); kstr_append_char(&str2, 'f'); kstr_append_char(&str2, 'o'); kstr_assign_cstr(&str1, "ob"); kstr_append_kstr(&str2, &str1); kstr_append_cstr(&str2, "ar"); assert(kstr_equal_cstr(&str2, "foobar")); kstr_free(&str1); kstr_free(&str2); kstr_free(&str3); }
static void test_khash() { khash h; kstr str; char *one = "one"; int a = 1; int nb1 = 3; int nb2 = 4; int nb3 = 3; khash_init(&h); kstr_init(&str); khash_set_func(&h, khash_cstr_key, khash_cstr_cmp); khash_add(&h, one, &a); assert(khash_get(&h, one) == &a); kstr_sf(&str, "%c%c%c", 'o', 'n', 'e'); assert(khash_get(&h, str.data) == &a); khash_clear(&h); khash_set_func(&h, khash_int_key, khash_int_cmp); khash_add(&h, &nb1, &nb1); khash_add(&h, &nb2, &nb2); assert(khash_get(&h, &nb3) == &nb1); khash_free(&h); kstr_free(&str); }
/* This function logs the content of a message that is sent / received from the * network. */ static void knp_log_msg(char *side, uint32_t major, uint32_t minor, uint32_t msg_type, kbuffer *payload, kstr *addr, uint32_t port) { assert(knp_log); int error = 0; kstr dump; kstr_init(&dump); error = knp_msg_dump(payload->data, payload->len, &dump); if (error) { fprintf(knp_log, "%s: badly formatted payload: %s.\n", side, kmo_strerror()); fwrite(payload->data, 1, payload->len, knp_log); } else { int cat = (msg_type & 0xff00) >> 8; int id = msg_type & 0xff; fprintf(knp_log, "%s version=%u,%u type=%u,%u len=%u address=%s port=%u>\n", side, major, minor, cat, id, payload->len, addr->data, port); fwrite(dump.data, 1, dump.slen, knp_log); } fprintf(knp_log, "\n"); kstr_free(&dump); }
/* Same as anp_dump(), but the header of the message is also dumped. */ int anp_msg_dump(struct anp_msg *self, kstr *dump_str) { int error = 0; kstr work_str; kstr_init(&work_str); kstr_reset(dump_str); kstr_sf(&work_str, "major> %u\n", self->major); kstr_append_kstr(dump_str, &work_str); kstr_sf(&work_str, "minor> %u\n", self->minor); kstr_append_kstr(dump_str, &work_str); kstr_sf(&work_str, "type> %u\n", self->minor); kstr_append_kstr(dump_str, &work_str); kstr_sf(&work_str, "id> "PRINTF_64"u\n", self->minor); kstr_append_kstr(dump_str, &work_str); if (anp_dump(&self->payload, &work_str)) { error = -1; } else { kstr_append_kstr(dump_str, &work_str); } kstr_clean(&work_str); return error; }
/* This function ensures that the string specified does not get too large. If * the internal memory associated to the string is bigger than the threshold * specified, the memory associated to the string is released and a new, small * buffer is allocated for the string. In all cases, the string is cleared. */ void kstr_shrink(kstr *self, int max_size) { if (self->slen > max_size) { kstr_clean(self); kstr_init(self); } kstr_reset(self); }
/* This function ensures that the string specified does not get too large. If * the internal memory associated to the string is bigger than the threshold * specified, the memory associated to the string is released and a new, small * buffer is allocated for the string. In all cases, the string is cleared * ('slen' is set to 0). */ void kstr_shrink(kstr *self, int max_size) { if (self->slen > max_size) { kstr_free(self); kstr_init(self); } self->slen = 0; }
static void test_util_bin_to_hex() { unsigned char in[4] = { 0xaf, 0x00, 0xfa, 0x0d }; kstr out; kstr_init(&out); util_bin_to_hex(in, 4, &out); assert(kstr_equal_cstr(&out, "af00fa0d")); kstr_free(&out); }
static void kdaemon_init() { global_opts.quit_flag = 0; global_opts.log_level = KCD_LOG_CRIT; global_opts.log_file = stderr; global_opts.remote_host = NULL; global_opts.local_host = NULL; global_opts.second_host = NULL; global_opts.remote_port = 0; global_opts.second_port = 0; global_opts.local_port = 0; global_opts.local_first = 0; kstr_init(&global_opts.ssl_cert_path); }
/* This function creates and initializes a KNP query. * The login OTUT must be set manually if it's needed. */ struct knp_query * knp_query_new(int contact, int login_type, int cmd_type, kbuffer *cmd_payload, kstr *all_req_str) { struct knp_query *query = (struct knp_query *) kmo_calloc(sizeof(struct knp_query)); query->contact = contact; kstr_init(&query->server_addr); query->login_type = login_type; query->cmd_type = cmd_type; query->cmd_payload = cmd_payload; kmo_data_transfer_init(&query->transfer); query->transfer.driver = kmo_sock_driver; kstr_assign_kstr(&query->all_req_str, all_req_str); return query; }
/* This function converts a path to an absolute path, if needed. */ void kpath_make_absolute(kstr *path, int format) { kstr tmp; /* Return if the path is already absolute. */ if (kpath_is_absolute(path, format)) return; kstr_init(&tmp); /* Get the current working directory and append the relative path to the * current working directory. */ kpath_getcwd(&tmp); kstr_append_kstr(&tmp, path); kstr_assign_kstr(path, &tmp); kstr_clean(&tmp); }
static void test_mail_parse_addr_field() { kstr addr_field; karray addr_array; char *john = "*****@*****.**"; int i; kstr_init(&addr_field); karray_init(&addr_array); kstr_sf(&addr_field, ";%s;;John Doe <%s>;<%s>", john, john, john); assert(! mail_parse_addr_field(&addr_field, &addr_array)); assert(addr_array.size == 3); for (i = 0; i < addr_array.size; i++) { assert(! strcmp(((kstr *) addr_array.data[i])->data, john)); } kmo_clear_kstr_array(&addr_array); kstr_sf(&addr_field, "<>"); assert(mail_parse_addr_field(&addr_field, &addr_array)); kstr_sf(&addr_field, "<"); assert(mail_parse_addr_field(&addr_field, &addr_array)); kstr_sf(&addr_field, ">"); assert(mail_parse_addr_field(&addr_field, &addr_array)); kstr_sf(&addr_field, "foobar"); assert(mail_parse_addr_field(&addr_field, &addr_array)); kstr_sf(&addr_field, "foobar@@example.com"); assert(mail_parse_addr_field(&addr_field, &addr_array)); kstr_sf(&addr_field, ">text<"); assert(mail_parse_addr_field(&addr_field, &addr_array)); kstr_sf(&addr_field, "foo;[email protected]"); assert(mail_parse_addr_field(&addr_field, &addr_array)); kstr_free(&addr_field); karray_free(&addr_array); }
/* This function returns the mail info having the hash and KSN specified. * This function sets the KMO error string. It returns -1 on general failure, * -2 if not found. */ static int maildb_sqlite_get_mail_info_from_hash(maildb *mdb, maildb_mail_info *mail_info, kstr *hash, kstr *ksn) { int error = 0; sqlite3 *db = (sqlite3 *) mdb->db; int64_t entry_id; error = get_entry_id_from_hash(db, hash, ksn, &entry_id); if (error) return -1; /* If we failed to find the mail, and a hash and a KSN were provided, we redo * the search with only the KSN, to find sent encrypted mails. */ if (entry_id == 0 && hash->slen > 0 && ksn->slen > 0) { kstr empty_hash; kstr_init(&empty_hash); error = get_entry_id_from_hash(db, &empty_hash, ksn, &entry_id); kstr_free(&empty_hash); if (error) return -1; } return maildb_sqlite_get_mail_info_from_entry_id(mdb, mail_info, entry_id); }
/* Replace all occurences of the string 'from' with the string 'to' in the * string specified. */ void kstr_replace(kstr *self, char *from, char *to) { kstr tmp; int i = 0; int l = strlen(from); assert(l); kstr_init(&tmp); while (i < self->slen) { if (! strncmp(self->data + i, from, l)) { kstr_append_cstr(&tmp, to); i += l; } else { kstr_append_char(&tmp, self->data[i]); i++; } } kstr_assign_kstr(self, &tmp); kstr_clean(&tmp); }
/* This function asks the proxy to connect us to the end server. * This function sets the KMO error string. It returns 0, -1, -2 or -3. */ static int knp_handle_proxy(struct knp_query *query, k3p_proto *k3p, kstr *proxy_login, kstr *proxy_pwd) { int error = 0; int first_line = 1; struct kmo_data_transfer *transfer = &query->transfer; kstr msg; kstr str; kmod_log_msg(3, "knp_handle_proxy() called.\n"); kstr_init(&msg); kstr_init(&str); /* Try. */ do { /* Connect to the specified server and port. */ kstr_sf(&msg, "CONNECT %s:%d HTTP/1.0\r\n", query->server_addr.data, query->server_port); /* Using the following "user:pwd" credentials encoded in base 64. */ if (proxy_login->slen > 0 || proxy_pwd->slen > 0) { kbuffer in, out; kbuffer_init(&in, 100); kbuffer_init(&out, 100); kbuffer_write(&in, proxy_login->data, proxy_login->slen); kbuffer_write(&in, ":", 1); kbuffer_write(&in, proxy_pwd->data, proxy_pwd->slen); bin2b64(&in, &out); kstr_append_cstr(&msg, "Proxy-Authorization: basic "); kstr_append_buf(&msg, out.data, out.len); kstr_append_cstr(&msg, "\r\n"); kbuffer_clean(&in); kbuffer_clean(&out); } /* An empty line marks the end of the request. */ kstr_append_cstr(&msg, "\r\n"); /* Send the message to the proxy. */ transfer->read_flag = 0; transfer->buf = msg.data; transfer->min_len = transfer->max_len = msg.slen; error = knp_exec_transfer(transfer, k3p); if (error) break; if (transfer->status != KMO_COMM_TRANS_COMPLETED) { assert(transfer->status == KMO_COMM_TRANS_ERROR); kmo_seterror("proxy error: %s", kmo_data_transfer_err(transfer)); knp_query_handle_conn_error(query, transfer->err_msg ? KMO_SERROR_MISC : KMO_SERROR_TIMEOUT); error = -1; break; } /* Receive the reply from the server, char by char to avoid reading past * the HTTP reply data. */ kstr_clear(&msg); while (1) { char c; transfer->read_flag = 1; transfer->buf = &c; transfer->min_len = transfer->max_len = 1; error = knp_exec_transfer(transfer, k3p); if (error) break; if (transfer->status != KMO_COMM_TRANS_COMPLETED) { assert(transfer->status == KMO_COMM_TRANS_ERROR); kmo_seterror("proxy error: %s", kmo_data_transfer_err(transfer)); knp_query_handle_conn_error(query, transfer->err_msg ? KMO_SERROR_MISC : KMO_SERROR_TIMEOUT); error = -1; break; } kstr_append_char(&msg, c); /* We reached the end of a line. */ if (msg.slen >= 2 && msg.data[msg.slen - 2] == '\r' && msg.data[msg.slen - 1] == '\n') { /* This is the response line. Parse it. */ if (first_line) { int reply_code; /* Expecting "HTTP/x.x ddd <string>\r\n" */ if (msg.slen < 16 || msg.data[0] != 'H' || msg.data[1] != 'T' || msg.data[2] != 'T' || msg.data[3] != 'P' || ! is_digit(msg.data[9]) || ! is_digit(msg.data[10]) || ! is_digit(msg.data[11])) { kmo_seterror("invalid proxy HTTP reply"); error = -1; break; } /* Get the numeric code. */ reply_code = atoi(msg.data + 9); /* Not 200, it didn't work. */ if (reply_code != 200) { /* Get the reason. */ kstr_assign_buf(&str, msg.data + 13, msg.slen - 2 - 13); kmo_seterror("the proxy refuses to connect: %s (code %d)", str.data, reply_code); error = -1; break; } first_line = 0; } /* The server is sending us some unwanted line. Ignore it. */ else if (msg.slen != 2) { /* Void. */ } /* We reached the end of the reply. The next bytes received will be SSL bytes. */ else { break; } /* Clear the line buffer. */ kstr_clear(&msg); } /* It's too long. */ else if (msg.slen > 1000) { kmo_seterror("HTTP reply too long"); error = -1; break; } } if (error) break; } while (0); kstr_free(&msg); kstr_free(&str); return error; }
void kmo_error_start() { kstr_init(&kmo_error_str); kstr_init(&kmo_scratch_str); }
kstr * kstr_new() { kstr *str = (kstr *) kmo_malloc(sizeof(kstr)); kstr_init(str); return str; }
void kstr_init_sfv(kstr *self, const char *format, va_list args) { kstr_init(self); kstr_sfv(self, format, args); }
kstr* kstr_new() { kstr *self = (kstr *) kmalloc(sizeof(kstr)); kstr_init(self); return self; }
static struct kthread_specific * kthread_specific_new() { struct kthread_specific *self = (struct kthread_specific *) kcalloc(sizeof(struct kthread_specific)); kerror_init(&self->error_stack); kstr_init(&self->err_str); return self; }
/* This function handles a connection in VNC proxy mode. */ static int kcd_frontend_handle_vnc(struct kcd_client *client) { int error = 0; int proxy_sock = -1; char begin_string[KCD_VNC_BEGIN_STRING_LENGTH + 1]; kstr cred_path; kdaemon_set_task("VNC proxy | %s", client->addr.data); kmod_log_msg(KCD_LOG_BRIEF, "kcd_frontend_handle_vnc() called.\n"); assert(strlen(KCD_VNC_TEST_KCD_VNC_BEGIN_STRING) == KCD_VNC_BEGIN_STRING_LENGTH); begin_string[KCD_VNC_BEGIN_STRING_LENGTH] = 0; kstr_init(&cred_path); do { int port; if (!global_opts.vnc_mode) { kmod_set_error("VNC proxy mode disabled"); error = -1; break; } /* Receive the file name. */ error = kcd_frontend_tls_xfer(&client->conn, begin_string, KCD_VNC_BEGIN_STRING_LENGTH, 1); if (error) break; /* This is a test. */ if (! strncmp(begin_string, KCD_VNC_TEST_KCD_VNC_BEGIN_STRING, KCD_VNC_BEGIN_STRING_LENGTH)) { kmod_log_msg(KCD_LOG_MISC, "kcd_proxy_handle_vnc(): test connection.\n"); error = kcd_frontend_tls_xfer(&client->conn, KCD_VNC_TEST_RESPONSE, KCD_VNC_TEST_RESPONSE_LENGTH, 0); if (error) break; } /* This is a genuine VNC connection. */ else { /* 'begin_string' is a credential file. */ char *file_name = begin_string; /* Check if that file exists. */ kstr_sf(&cred_path, "%s/%s", global_opts.vnc_cred_path.data, file_name); if (! kfs_regular(cred_path.data)) { kmod_set_error("credential file %s does not exist", file_name); error = -1; break; } /* Get the port. */ port = atoi(file_name + KCD_VNC_PORT_OFFSET); /* Note: don't delete the credential file, IE sucks and connects * twice. */ /* Connect to the VNC proxy. */ error = kproxy_connect_tcp(&proxy_sock, global_opts.kcd_host.data, port); if (error) break; /* Loop within the proxy. */ error = kcd_frontend_proxy_loop(&client->conn, proxy_sock, "VNC proxy"); if (error) break; } } while (0); ksock_close(&proxy_sock); kstr_clean(&cred_path); return error; }
/* This function starts the VNC session. */ int kcd_vnc_start_session(struct kcd_ticket_mode_state *tms) { int error = 0; struct kcd_vnc_mode_state vms; kstr subject; kbuffer *kbb = &tms->kws_bound_buf, *in_buf = &tms->aq.input_buf, *out_buf = &tms->aq.output_buf; kcd_vnc_mode_state_init(&vms, tms); kstr_init(&subject); do { /* Check the right to start/join a VNC session. */ error = kcd_vnc_check_right(tms); if (error) break; /* Get the extra information from the command. */ if (anp_read_kstr(&tms->in_msg->payload, &subject)) { error = -2; break; } /* Start the proxy server. */ error = kcd_vnc_start_proxy(&vms); if (error) break; /* Start the session in Postgres. This has to be done after the proxy * has been started so that the session does not get collected early. */ anp_write_kstr(kbb, &subject); anp_write_uint32(kbb, vms.proxy_port); error = kcd_ticket_mode_kws_bound_query(tms, "start_vnc", ktime_now_sec(), NULL); if (error) break; error = anp_read_uint64(out_buf, &vms.session_id); if (error) break; /* Send the "OK" to the client. */ kcd_ticket_mode_new_out_msg(tms, KANP_RES_OK); if (tms->client->effective_minor > 2) { tms->out_msg->type = KANP_RES_VNC_START_SESSION; anp_write_uint64(&tms->out_msg->payload, vms.session_id); } error = kcd_ticket_mode_send_msg(tms); if (error) break; /* Loop communicating with the proxy. */ error = kcd_vnc_proxy_comm_loop(&vms); if (error) break; /* Terminate the session. */ anp_write_uint64(in_buf, tms->kws_id); anp_write_uint32(in_buf, tms->user_id); anp_write_uint64(in_buf, vms.session_id); anp_write_uint32(in_buf, tms->client->effective_minor >= 5 ? 5 : 2); anp_write_uint32(in_buf, vms.end_error_code); anp_write_kstr(in_buf, &vms.end_error_msg); error = kcd_exec_safe_pg_anp_query(&tms->db_conn, &tms->aq, "end_vnc"); if (error) break; } while (0); /* Kill and collect the proxy server. */ if (error != -1) { if (kcd_process_kill_and_collect(&vms.process)) { error = -1; } } /* Log the proxy output. */ kcd_process_log_output(&vms.process, 1, 1); kcd_vnc_mode_state_clean(&vms); kstr_clean(&subject); return error; }
/* This function negociates a SSL session with the server. * This function sets the KMO error string. It returns 0, -1, -2, or -3. */ static int knp_negociate_ssl_session(struct knp_query *query, char *cert, k3p_proto *k3p) { int error = 0; SSL_METHOD *ssl_method; BIO *ssl_bio; kmod_log_msg(3, "knp_negociate_ssl_session() called.\n"); /* Create the SSL driver. */ struct knp_ssl_driver *driver = (struct knp_ssl_driver *) kmo_calloc(sizeof(struct knp_ssl_driver)); assert(query->ssl_driver == NULL); query->ssl_driver = driver; ssl_method = SSLv3_client_method(); if (ssl_method == NULL) { kmo_seterror("cannot initialize SSL method"); return -1; } driver->ssl_ctx = SSL_CTX_new(ssl_method); if (driver->ssl_ctx == NULL) { kmo_seterror("cannot initialize SSL context"); return -1; } driver->ssl = SSL_new(driver->ssl_ctx); if (driver->ssl == NULL) { kmo_seterror("cannot initialize SSL session"); return -1; } ssl_bio = BIO_new_socket(query->transfer.fd, BIO_NOCLOSE); if (ssl_bio == NULL) { kmo_seterror("cannot initialize SSL BIO"); return -1; } /* Set SSL BIO. 'ssl_bio' is owned by 'ssl', do not free. */ SSL_set_bio(driver->ssl, ssl_bio, ssl_bio); /* If we have a certificate, set it in SSL. */ if (cert) { int i; BIO *cert_bio = NULL; X509 *cert_obj = NULL; X509_STORE *cert_store = NULL; X509_OBJECT x509_obj; kstr cert_buf; kstr_init(&cert_buf); /* Try. */ do { /* Recreate the certificate. */ kstr_append_cstr(&cert_buf, "-----BEGIN CERTIFICATE-----\n"); kstr_append_cstr(&cert_buf, cert); kstr_append_cstr(&cert_buf, "-----END CERTIFICATE-----\n"); for (i = 0; i < cert_buf.slen; i++) { if (cert_buf.data[i] == '|') { cert_buf.data[i] = '\n'; } } /* Put the certificate text in a buffer. */ cert_bio = BIO_new_mem_buf(cert_buf.data, cert_buf.slen); if (cert_bio == NULL) { kmo_seterror("cannot create SSL BIO for reading SSL certificate"); error = -1; break; } /* Create the certificate object with the buffer data. */ cert_obj = PEM_read_bio_X509(cert_bio, NULL, 0, NULL); if (cert_obj == NULL) { kmo_seterror("cannot create SSL certificate"); error = -1; break; } /* Add the certificate in the certificate store if it is not * already present. */ /* Get the certificate store. */ cert_store = SSL_CTX_get_cert_store(driver->ssl_ctx); if (cert_store == NULL) { kmo_seterror("cannot get SSL certificate store"); error = -1; break; } /* Understanding SSL's API is a daunting task. I've peeked at * the source code and that code should work. For now. Sigh. * Wouldn't it be nice if programmers bothered to make * *library* APIs that are somewhat sane... * * Check if the certificate is already in the store. */ x509_obj.type = X509_LU_X509; x509_obj.data.x509 = cert_obj; /* It seems the certificate is already in the store. */ if (X509_OBJECT_retrieve_match(cert_store->objs, &x509_obj)) { /* Void. */ } /* Add the certificate in the store. We still own cert_obj after * this call. */ else if (X509_STORE_add_cert(cert_store, cert_obj) != 1) { kmo_seterror("cannot store SSL certificate"); error = -1; break; } } while (0); if (cert_obj) X509_free(cert_obj); if (cert_bio) BIO_free(cert_bio); kstr_free(&cert_buf); if (error) return error; } /* If we need a certificate, require the server to send us its certificate. */ SSL_set_verify(driver->ssl, cert ? SSL_VERIFY_PEER : SSL_VERIFY_NONE, NULL); /* Loop until we connect or fail. */ while (1) { error = SSL_connect(driver->ssl); /* We're connected. */ if (error > 0) { error = 0; break; } else { int ssl_error = SSL_get_error(driver->ssl, error); error = 0; /* SSL wants us to wait for reading data. */ if (ssl_error == SSL_ERROR_WANT_READ) { error = knp_query_wait_for_data(query, 1, "SSL negociation read failed", k3p); if (error) return error; } /* SSL wants us to wait for writing data. */ else if (ssl_error == SSL_ERROR_WANT_WRITE) { error = knp_query_wait_for_data(query, 0, "SSL negociation write failed", k3p); if (error) return error; } /* Life is tough. */ else { kmo_seterror("SSL negociation failed: %s", get_ssl_error_string(ssl_error)); return -1; } } } /* Validate the server's certificate as needed. */ if (cert) { /* Check if the server sent us a certificate. */ X509 *peer_cert = SSL_get_peer_certificate(driver->ssl); if (peer_cert == NULL) { kmo_seterror("the server did not send its SSL certificate"); return -1; } X509_free(peer_cert); /* Verify the certificate. */ if (SSL_get_verify_result(driver->ssl) != X509_V_OK) { kmo_seterror("the SSL certificate of the server is invalid"); return -1; } } return 0; }
/* This function connects to the specified server (possibly through a proxy) and * negociates a SSL session. REMARK: a backport was applied to this function. It * was ugly in the first place, the backport didn't help any. All of it is * rewritten in the next version. REMARK 2: whooops, modified it again. It's * getting prettier by the minute. * This function sets the KMO error string. It returns 0, -1, -2, or -3. */ static int knp_query_connect(struct knp_query *self, struct knp_proto *knp) { assert(self->transfer.fd == -1); assert(self->ssl_driver == NULL); kmod_log_msg(3, "knp_query_connect() called.\n"); int error = 0; kstr str; int use_srv = 0; int use_proxy = 0; kstr proxy_addr; uint32_t proxy_port = 0; kstr proxy_login; kstr proxy_pwd; char *cert = NULL; struct kmo_data_transfer *transfer = &self->transfer; kstr_init(&str); kstr_init(&proxy_addr); kstr_init(&proxy_login); kstr_init(&proxy_pwd); /* Try. */ do { /* We're contacting the KPS. */ if (self->contact == KNP_CONTACT_KPS && k3p_is_using_kps(knp->server_info)) { kstr_assign_kstr(&self->server_addr, &knp->server_info->kps_net_addr); self->server_port = knp->server_info->kps_port_num; if (knp->server_info->kps_use_proxy) { use_proxy = 1; kstr_assign_kstr(&proxy_addr, &knp->server_info->kps_proxy_net_addr); proxy_port = knp->server_info->kps_proxy_port_num; kstr_assign_kstr(&proxy_login, &knp->server_info->kps_proxy_login); kstr_assign_kstr(&proxy_pwd, &knp->server_info->kps_proxy_pwd); } } /* We're contacting the KOS. */ else { #ifdef __DEBUG_KOS_ADDRESS__ kstr_assign_cstr(&self->server_addr, __DEBUG_KOS_ADDRESS__); if (ops_address || ous_address || ots_address || iks_address || eks_address) {} #else /* This is the option to use a one-stop server for all online service requests. */ if (self->all_req_str.slen > 0) { kstr_assign_kstr(&self->server_addr, &self->all_req_str); } else if (knp->server_info->kos_use_proxy || knp->use_kpg) { switch (self->contact) { case KNP_CONTACT_KPS: kstr_assign_cstr(&self->server_addr, ops_address); break; case KNP_CONTACT_OPS: kstr_assign_cstr(&self->server_addr, ops_address); break; case KNP_CONTACT_OUS: kstr_assign_cstr(&self->server_addr, ous_address); break; case KNP_CONTACT_OTS: kstr_assign_cstr(&self->server_addr, ots_address); break; case KNP_CONTACT_IKS: kstr_assign_cstr(&self->server_addr, iks_address); break; case KNP_CONTACT_EKS: kstr_assign_cstr(&self->server_addr, eks_address); break; default: assert(0); }; if (knp->use_kpg) { switch (self->contact) { case KNP_CONTACT_OPS: kstr_assign_cstr(&self->server_addr, knp->kpg_addr.data); break; case KNP_CONTACT_OUS: kstr_assign_cstr(&self->server_addr, knp->kpg_addr.data); break; case KNP_CONTACT_OTS: kstr_assign_cstr(&self->server_addr, knp->kpg_addr.data); break; }; } } else { use_srv = 1; switch (self->contact) { case KNP_CONTACT_KPS: kstr_assign_cstr(&self->server_addr, srv_ops_address); break; case KNP_CONTACT_OPS: kstr_assign_cstr(&self->server_addr, srv_ops_address); break; case KNP_CONTACT_OUS: kstr_assign_cstr(&self->server_addr, srv_ous_address); break; case KNP_CONTACT_OTS: kstr_assign_cstr(&self->server_addr, srv_ots_address); break; case KNP_CONTACT_IKS: kstr_assign_cstr(&self->server_addr, srv_iks_address); break; case KNP_CONTACT_EKS: kstr_assign_cstr(&self->server_addr, srv_eks_address); break; default: assert(0); }; } #endif #ifdef __DEBUG_KOS_PORT__ self->server_port = __DEBUG_KOS_PORT__; if (kos_port) {} #else self->server_port = kos_port; if (knp->use_kpg) { switch (self->contact) { case KNP_CONTACT_OPS: self->server_port = knp->kpg_port; break; case KNP_CONTACT_OUS: self->server_port = knp->kpg_port; break; case KNP_CONTACT_OTS: self->server_port = knp->kpg_port; break; }; } #endif if (knp->server_info->kos_use_proxy) { use_proxy = 1; kstr_assign_kstr(&proxy_addr, &knp->server_info->kos_proxy_net_addr); proxy_port = knp->server_info->kos_proxy_port_num; kstr_assign_kstr(&proxy_login, &knp->server_info->kos_proxy_login); kstr_assign_kstr(&proxy_pwd, &knp->server_info->kos_proxy_pwd); } #ifdef NDEBUG cert = kos_cert; #else if (kos_cert) {} #endif } /* Validate our contact information. */ if (self->server_addr.slen == 0 || self->server_port == 0) { kmo_seterror("invalid server information"); knp_query_handle_conn_error(self, KMO_SERROR_MISC); error = -1; break; } if (use_proxy && (proxy_addr.slen == 0 || proxy_port == 0)) { kmo_seterror("invalid proxy information"); knp_query_handle_conn_error(self, KMO_SERROR_MISC); error = -1; break; } /* Connect to the proxy or the end server directly. */ if (! use_srv) { error = kmo_sock_create(&transfer->fd); if (error) break; error = kmo_sock_set_unblocking(transfer->fd); if (error) break; error = kmo_sock_connect(transfer->fd, use_proxy ? proxy_addr.data : self->server_addr.data, use_proxy ? proxy_port : self->server_port); if (error) { knp_query_handle_conn_error(self, KMO_SERROR_UNREACHABLE); break; } kstr_sf(&str, "cannot connect to %s", use_proxy ? proxy_addr.data : self->server_addr.data); error = knp_query_wait_for_data(self, 0, str.data, knp->k3p); if (error) { if (error == -1) self->serv_error_id = KMO_SERROR_UNREACHABLE; break; } error = kmo_sock_connect_check(transfer->fd, use_proxy ? proxy_addr.data : self->server_addr.data); if (error) { knp_query_handle_conn_error(self, KMO_SERROR_UNREACHABLE); break; } /* If there is a proxy, asks it to connect us to the end server. */ if (use_proxy) { error = knp_handle_proxy(self, knp->k3p, &proxy_login, &proxy_pwd); if (error) break; } } /* Connect using the SRV entries. */ else { error = knp_query_srv_connect(self, knp, self->server_addr.data); if (error) break; } /* Negociate the SSL session. */ error = knp_negociate_ssl_session(self, cert, knp->k3p); if (error) break; } while (0); if (error) knp_query_disconnect(self); kstr_free(&str); kstr_free(&proxy_addr); kstr_free(&proxy_login); kstr_free(&proxy_pwd); return error; }
/* This function dumps the content of a KNP message buffer in the string * specified. This function sets the KMO error string when it encounters an * error in the buffer. It returns -1 on failure. */ int knp_msg_dump(char *buf, int buf_len, kstr *dump_str) { int error = 0; int pos = 0; uint32_t u32; uint64_t u64; kstr work_str; kstr_init(&work_str); kstr_clear(dump_str); while (pos < buf_len) { uint8_t type = buf[pos]; pos++; if (type == KNP_UINT32) { if (pos + 4 > buf_len) { kmo_seterror("uint32 specified but not included"); error = -1; break; } memcpy(&u32, buf + pos, 4); kstr_sf(&work_str, "uint32> %u\n", ntohl(u32)); kstr_append_kstr(dump_str, &work_str); pos += 4; } else if (type == KNP_UINT64) { if (pos + 8 > buf_len) { kmo_seterror("uint64 specified but not included"); error = -1; break; } memcpy(&u64, buf + pos, 8); kstr_sf(&work_str, "uint64> %llu\n", ntohll(u64)); kstr_append_kstr(dump_str, &work_str); pos += 8; } else if (type == KNP_STR) { if (pos + 4 > buf_len) { kmo_seterror("string specified but not included"); error = -1; break; } memcpy(&u32, buf + pos, 4); pos += 4; u32 = ntohl(u32); if (pos + u32 > (uint32_t) buf_len) { kmo_seterror("string specified but not included"); error = -1; break; } kstr_sf(&work_str, "string %u> ", u32); kstr_append_kstr(dump_str, &work_str); kstr_append_buf(dump_str, buf + pos, u32); kstr_append_cstr(dump_str, "\n"); pos += u32; } else { kmo_seterror("invalid KNP identifier (%u)", type); error = -1; break; } } kstr_free(&work_str); return error; }