/* FIXME: In Blackjack this function should become generally available. */ void format_current_error_for_user(kstr *dest) { struct kerror *error_instance = kerror_get_current(); int i; kstr_reset(dest); if (error_instance->stack.size == 0) { kstr_assign_cstr(dest, "unknown error"); } else { for (i = error_instance->stack.size - 1; i >= 0; i--) { struct kerror_node *node = (struct kerror_node *) karray_get(&error_instance->stack, i); /* Level 1 error are meant to be readable. */ if (node->level >= 1) { if (i != error_instance->stack.size - 1) { kstr_append_cstr(dest, ": "); } kstr_append_kstr(dest, &node->text); } } } if (dest->slen == 0) kstr_assign_cstr(dest, "unknown error"); }
kstr *kdclient_format_error(const char *file, int line, const char *msg, va_list va) { char *p; kstr *err_msg, *err; kstr err_fmt; /* Search for @ */ p = strstr(msg, "@"); if (p) { kstr_init_buf(&err_fmt, msg, p - msg); err_msg = kerror_str_n(0); kstr_append_kstr(&err_fmt, err_msg); kstr_destroy(err_msg); kstr_append_cstr(&err_fmt, p + 1); } else kstr_init_cstr(&err_fmt, msg); err = kmalloc(sizeof(kstr)); kstr_init_sfv(err, err_fmt.data, va); /* Add [file:line] before error message. */ kstr_sf(&err_fmt, "[%s:%d] %s", file, line, err->data); kstr_assign_kstr(err, &err_fmt); kstr_clean(&err_fmt); return err; }
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_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); }
/** Converts a DNS name to a domain DN. * * Example: AD.LOCAL => DC=AD,DC=LOCAL. * * This is very much AD specific. It won't work for Lotus Domino DNs. */ char *kdldap_DNS_to_dn(apr_pool_t *pool, const char *dns_dom) { kstr r; const char *s = dns_dom; char *dn; kstr_init_cstr(&r, "DC="); while (*s) { if (*s == '.') kstr_append_cstr(&r, ",DC="); else kstr_append_char(&r, *s); s++; } dn = apr_pstrdup(pool, r.data); kstr_clean(&r); return dn; }
/* 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 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 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; }
/* 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; }
/* This function creates a mail_eval_res entry. * This function sets the KMO error string. It returns -1 on failure. */ static int create_mail_eval_res(sqlite3 *db, maildb_mail_info *mail_info, int64_t *entry_id) { sqlite3_stmt *stmt = NULL; kstr insert_str; int i; assert(*entry_id >= 0); /* Create the mail_eval_res entry. */ kstr_init_cstr(&insert_str, "INSERT INTO mail_eval_res3 ("); /* If entry_id is not 0, then we must specify the entry_id in the INSERT. */ if (*entry_id != 0) { kstr_append_cstr(&insert_str, "entry_id, "); } kstr_append_cstr(&insert_str, "hash, ksn, status, display_pref, sig_msg, mid, original_packaging, mua, field_status, " "att_plugin_nbr, attachment_nbr, attachment_status, sym_key, encryption_status, " "decryption_error_msg, pod_status, pod_msg, otut_status, otut_string, " "otut_msg, kpg_addr, kpg_port) " "VALUES ("); if (*entry_id != 0) { kstr_append_cstr(&insert_str, "?, "); } kstr_append_cstr(&insert_str, "?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"); if (sqlite3_prepare(db, insert_str.data, -1, &stmt, NULL)) goto ERR; i = 1; if (*entry_id != 0) { if (sqlite3_bind_int64(stmt, i++, *entry_id)) goto ERR; } if (write_blob(stmt, i++, &mail_info->hash)) goto ERR; if (write_blob(stmt, i++, &mail_info->ksn)) goto ERR; if (sqlite3_bind_int(stmt, i++, mail_info->status)) goto ERR; if (sqlite3_bind_int(stmt, i++, mail_info->display_pref)) goto ERR; if (write_string(stmt, i++, &mail_info->sig_msg)) goto ERR; if (sqlite3_bind_int64(stmt, i++, mail_info->mid)) goto ERR; if (sqlite3_bind_int(stmt, i++, mail_info->original_packaging)) goto ERR; if (sqlite3_bind_int(stmt, i++, mail_info->mua)) goto ERR; if (sqlite3_bind_int(stmt, i++, mail_info->field_status)) goto ERR; if (sqlite3_bind_int(stmt, i++, mail_info->att_plugin_nbr)) goto ERR; if (sqlite3_bind_int(stmt, i++, mail_info->attachment_nbr)) goto ERR; if (write_blob(stmt, i++, &mail_info->attachment_status)) goto ERR; if (write_blob(stmt, i++, &mail_info->sym_key)) goto ERR; if (sqlite3_bind_int(stmt, i++, mail_info->encryption_status)) goto ERR; if (write_string(stmt, i++, &mail_info->decryption_error_msg)) goto ERR; if (sqlite3_bind_int(stmt, i++, mail_info->pod_status)) goto ERR; if (write_string(stmt, i++, &mail_info->pod_msg)) goto ERR; if (sqlite3_bind_int(stmt, i++, mail_info->otut_status)) goto ERR; if (write_blob(stmt, i++, &mail_info->otut_string)) goto ERR; if (write_string(stmt, i++, &mail_info->otut_msg)) goto ERR; if (write_string(stmt, i++, &mail_info->kpg_addr)) goto ERR; if (sqlite3_bind_int(stmt, i++, mail_info->kpg_port)) goto ERR; if (sqlite3_step(stmt) != SQLITE_DONE) goto ERR; finalize_stmt(db, &stmt); /* Obtain/validate the entry ID. */ if (*entry_id == 0) { *entry_id = sqlite3_last_insert_rowid(db); } else { assert(*entry_id == sqlite3_last_insert_rowid(db)); } kstr_free(&insert_str); return 0; ERR: kmo_seterror(sqlite3_errmsg(db)); finalize_stmt(db, &stmt); kstr_free(&insert_str); return -1; }
/* This function dumps the content of a ANP message buffer in the string * specified. This function sets the KMOD error string when it encounters an * error in the buffer. It returns -1 on failure. */ int anp_dump(kbuffer *buf, kstr *dump_str) { int error = 0; kstr work_str; kstr data_str; kbuffer bin_buf; kstr_init(&work_str); kstr_init(&data_str); kbuffer_init(&bin_buf); kstr_reset(dump_str); while (! kbuffer_eof(buf)) { uint8_t type = buf->data[buf->pos]; if (type == ANP_UINT32) { uint32_t val; error = anp_read_uint32(buf, &val); if (error) break; kstr_sf(&work_str, "uint32> %u\n", val); kstr_append_kstr(dump_str, &work_str); } else if (type == ANP_UINT64) { uint64_t val; error = anp_read_uint64(buf, &val); if (error) break; kstr_sf(&work_str, "uint64> "PRINTF_64"u\n", val); kstr_append_kstr(dump_str, &work_str); } else if (type == ANP_STR) { error = anp_read_kstr(buf, &data_str); if (error) break; kstr_sf(&work_str, "string %u> ", data_str.slen); kstr_append_kstr(dump_str, &work_str); kstr_append_kstr(dump_str, &data_str); kstr_append_cstr(dump_str, "\n"); } else if (type == ANP_BIN) { error = anp_read_bin(buf, &bin_buf); if (error) break; kstr_sf(&work_str, "binary %u> ", bin_buf.len); kstr_append_kstr(dump_str, &work_str); kstr_append_cstr(dump_str, "\n"); } else { kmod_set_error("invalid ANP identifier (%u)\n", type); error = -1; break; } } kstr_clean(&work_str); kstr_clean(&data_str); kbuffer_clean(&bin_buf); /* Reset the buffer position to 0. */ buf->pos = 0; return error; }