static gint rspamd_control_finish_handler (struct rspamd_http_connection *conn, struct rspamd_http_message *msg) { struct ucl_parser *parser; ucl_object_t *obj; rspamd_fstring_t *out; const gchar *body; gsize body_len; struct rspamadm_control_cbdata *cbdata = conn->ud; body = rspamd_http_message_get_body (msg, &body_len); parser = ucl_parser_new (0); if (!body || !ucl_parser_add_chunk (parser, body, body_len)) { rspamd_fprintf (stderr, "cannot parse server's reply: %s\n", ucl_parser_get_error (parser)); ucl_parser_free (parser); } else { obj = ucl_parser_get_object (parser); out = rspamd_fstring_new (); if (json) { rspamd_ucl_emit_fstring (obj, UCL_EMIT_JSON, &out); } else if (compact) { rspamd_ucl_emit_fstring (obj, UCL_EMIT_JSON_COMPACT, &out); } else { if (strcmp (cbdata->path, "/fuzzystat") == 0) { rspamadm_execute_lua_ucl_subr (cbdata->L, cbdata->argc, cbdata->argv, obj, rspamadm_script_fuzzy_stat); rspamd_fstring_free (out); ucl_object_unref (obj); ucl_parser_free (parser); return 0; } else { rspamd_ucl_emit_fstring (obj, UCL_EMIT_CONFIG, &out); } } rspamd_fprintf (stdout, "%V", out); rspamd_fstring_free (out); ucl_object_unref (obj); ucl_parser_free (parser); } return 0; }
static gint lua_cryptobox_signature_gc (lua_State *L) { rspamd_fstring_t *sig = lua_check_cryptobox_sign (L, 1); rspamd_fstring_free (sig); return 0; }
/** * Sign file using specified rsa key and signature * * arguments: * (rsa_privkey, rsa_signature, string) * * returns: * true - if string match rsa signature * false - otherwise */ static gint lua_rsa_sign_file (lua_State *L) { RSA *rsa; rspamd_fstring_t *signature, **psig; const gchar *filename; gchar *data = NULL, *data_sig; gint ret, fd; struct stat st; rsa = lua_check_rsa_privkey (L, 1); filename = luaL_checkstring (L, 2); if (rsa != NULL && filename != NULL) { fd = open (filename, O_RDONLY); if (fd == -1) { msg_err ("cannot open file %s: %s", filename, strerror (errno)); lua_pushnil (L); } else { if (fstat (fd, &st) == -1 || (data = mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) { msg_err ("cannot mmap file %s: %s", filename, strerror (errno)); lua_pushnil (L); } else { signature = rspamd_fstring_sized_new (RSA_size (rsa)); data_sig = g_compute_checksum_for_string (G_CHECKSUM_SHA256, data, st.st_size); ret = RSA_sign (NID_sha1, data_sig, strlen (data_sig), signature->str, (guint *)&signature->len, rsa); if (ret == 0) { msg_info ("cannot make a signature for data: %s", ERR_error_string (ERR_get_error (), NULL)); lua_pushnil (L); rspamd_fstring_free (signature); } else { psig = lua_newuserdata (L, sizeof (rspamd_fstring_t *)); rspamd_lua_setclass (L, "rspamd{rsa_signature}", -1); *psig = signature; } g_free (data_sig); munmap (data, st.st_size); } close (fd); } } else { lua_pushnil (L); } return 1; }
static void rspamadm_confighelp_show (struct rspamd_config *cfg, gint argc, gchar **argv, const char *key, const ucl_object_t *obj) { rspamd_fstring_t *out; rspamd_lua_set_path (cfg->lua_state, NULL, ucl_vars); out = rspamd_fstring_new (); if (json) { rspamd_ucl_emit_fstring (obj, UCL_EMIT_JSON, &out); } else if (compact) { rspamd_ucl_emit_fstring (obj, UCL_EMIT_JSON_COMPACT, &out); } else { /* TODO: add lua helper for output */ if (key) { rspamd_fprintf (stdout, "Showing help for %s%s:\n", keyword ? "keyword " : "", key); } else { rspamd_fprintf (stdout, "Showing help for all options:\n"); } rspamadm_execute_lua_ucl_subr (argc, argv, obj, "confighelp", TRUE); rspamd_fstring_free (out); return; } rspamd_fprintf (stdout, "%V", out); rspamd_fprintf (stdout, "\n"); rspamd_fstring_free (out); }
static void rspamadm_dump_section_obj (const ucl_object_t *obj) { rspamd_fstring_t *output; output = rspamd_fstring_new (); if (json) { rspamd_ucl_emit_fstring (obj, UCL_EMIT_JSON, &output); } else if (compact) { rspamd_ucl_emit_fstring (obj, UCL_EMIT_JSON_COMPACT, &output); } else { rspamd_ucl_emit_fstring (obj, UCL_EMIT_CONFIG, &output); } rspamd_printf ("%V", output); rspamd_fstring_free (output); }
/** * Sign memory using specified rsa key and signature * * arguments: * (rsa_privkey, string) * * returns: * rspamd_signature object * nil - otherwise */ static gint lua_rsa_sign_memory (lua_State *L) { RSA *rsa; rspamd_fstring_t *signature, **psig; const gchar *data; guchar *data_sig; gint ret; rsa = lua_check_rsa_privkey (L, 1); data = luaL_checkstring (L, 2); if (rsa != NULL && data != NULL) { signature = rspamd_fstring_sized_new (RSA_size (rsa)); data_sig = g_compute_checksum_for_string (G_CHECKSUM_SHA256, data, -1); ret = RSA_sign (NID_sha1, data_sig, strlen (data_sig), signature->str, (guint *)&signature->len, rsa); if (ret == 0) { msg_info ("cannot make a signature for data: %s", ERR_error_string (ERR_get_error (), NULL)); lua_pushnil (L); rspamd_fstring_free (signature); } else { psig = lua_newuserdata (L, sizeof (rspamd_fstring_t *)); rspamd_lua_setclass (L, "rspamd{rsa_signature}", -1); *psig = signature; } g_free (data_sig); } else { lua_pushnil (L); } return 1; }
void rspamd_re_cache_init (struct rspamd_re_cache *cache, struct rspamd_config *cfg) { guint i, fl; GHashTableIter it; gpointer k, v; struct rspamd_re_class *re_class; rspamd_cryptobox_hash_state_t st_global; rspamd_regexp_t *re; struct rspamd_re_cache_elt *elt; guchar hash_out[rspamd_cryptobox_HASHBYTES]; g_assert (cache != NULL); rspamd_cryptobox_hash_init (&st_global, NULL, 0); /* Resort all regexps */ g_ptr_array_sort (cache->re, rspamd_re_cache_sort_func); for (i = 0; i < cache->re->len; i ++) { elt = g_ptr_array_index (cache->re, i); re = elt->re; re_class = rspamd_regexp_get_class (re); g_assert (re_class != NULL); rspamd_regexp_set_cache_id (re, i); if (re_class->st == NULL) { re_class->st = g_slice_alloc (sizeof (*re_class->st)); rspamd_cryptobox_hash_init (re_class->st, NULL, 0); } /* Update hashes */ rspamd_cryptobox_hash_update (re_class->st, (gpointer) &re_class->id, sizeof (re_class->id)); rspamd_cryptobox_hash_update (&st_global, (gpointer) &re_class->id, sizeof (re_class->id)); rspamd_cryptobox_hash_update (re_class->st, rspamd_regexp_get_id (re), rspamd_cryptobox_HASHBYTES); rspamd_cryptobox_hash_update (&st_global, rspamd_regexp_get_id (re), rspamd_cryptobox_HASHBYTES); fl = rspamd_regexp_get_pcre_flags (re); rspamd_cryptobox_hash_update (re_class->st, (const guchar *)&fl, sizeof (fl)); rspamd_cryptobox_hash_update (&st_global, (const guchar *) &fl, sizeof (fl)); fl = rspamd_regexp_get_flags (re); rspamd_cryptobox_hash_update (re_class->st, (const guchar *) &fl, sizeof (fl)); rspamd_cryptobox_hash_update (&st_global, (const guchar *) &fl, sizeof (fl)); fl = rspamd_regexp_get_maxhits (re); rspamd_cryptobox_hash_update (re_class->st, (const guchar *) &fl, sizeof (fl)); rspamd_cryptobox_hash_update (&st_global, (const guchar *) &fl, sizeof (fl)); } rspamd_cryptobox_hash_final (&st_global, hash_out); rspamd_snprintf (cache->hash, sizeof (cache->hash), "%*xs", (gint) rspamd_cryptobox_HASHBYTES, hash_out); /* Now finalize all classes */ g_hash_table_iter_init (&it, cache->re_classes); while (g_hash_table_iter_next (&it, &k, &v)) { re_class = v; if (re_class->st) { /* * We finally update all classes with the number of expressions * in the cache to ensure that if even a single re has been changed * we won't be broken due to id mismatch */ rspamd_cryptobox_hash_update (re_class->st, (gpointer)&cache->re->len, sizeof (cache->re->len)); rspamd_cryptobox_hash_final (re_class->st, hash_out); rspamd_snprintf (re_class->hash, sizeof (re_class->hash), "%*xs", (gint) rspamd_cryptobox_HASHBYTES, hash_out); g_slice_free1 (sizeof (*re_class->st), re_class->st); re_class->st = NULL; } } #ifdef WITH_HYPERSCAN const gchar *platform = "generic"; rspamd_fstring_t *features = rspamd_fstring_new (); cache->disable_hyperscan = cfg->disable_hyperscan; cache->vectorized_hyperscan = cfg->vectorized_hyperscan; g_assert (hs_populate_platform (&cache->plt) == HS_SUCCESS); /* Now decode what we do have */ switch (cache->plt.tune) { case HS_TUNE_FAMILY_HSW: platform = "haswell"; break; case HS_TUNE_FAMILY_SNB: platform = "sandy"; break; case HS_TUNE_FAMILY_BDW: platform = "broadwell"; break; case HS_TUNE_FAMILY_IVB: platform = "ivy"; break; default: break; } if (cache->plt.cpu_features & HS_CPU_FEATURES_AVX2) { features = rspamd_fstring_append (features, "AVX2", 4); } hs_set_allocator (g_malloc, g_free); msg_info_re_cache ("loaded hyperscan engine witch cpu tune '%s' and features '%V'", platform, features); rspamd_fstring_free (features); #endif }
rspamd_shingles_from_text (GArray *input, const guchar key[16], rspamd_mempool_t *pool, rspamd_shingles_filter filter, gpointer filterd, enum rspamd_shingle_alg alg) { struct rspamd_shingle *res; guint64 **hashes; guchar **keys; rspamd_fstring_t *row; rspamd_stat_token_t *word; guint64 val; gint i, j, k; gsize hlen, beg = 0; enum rspamd_cryptobox_fast_hash_type ht; if (pool != NULL) { res = rspamd_mempool_alloc (pool, sizeof (*res)); } else { res = g_malloc (sizeof (*res)); } row = rspamd_fstring_sized_new (256); /* Init hashes pipes and keys */ hashes = g_malloc (sizeof (*hashes) * RSPAMD_SHINGLE_SIZE); hlen = input->len > SHINGLES_WINDOW ? (input->len - SHINGLES_WINDOW + 1) : 1; keys = rspamd_shingles_get_keys_cached (key); for (i = 0; i < RSPAMD_SHINGLE_SIZE; i ++) { hashes[i] = g_malloc (hlen * sizeof (guint64)); } /* Now parse input words into a vector of hashes using rolling window */ if (alg == RSPAMD_SHINGLES_OLD) { for (i = 0; i <= (gint)input->len; i ++) { if (i - beg >= SHINGLES_WINDOW || i == (gint)input->len) { for (j = beg; j < i; j ++) { word = &g_array_index (input, rspamd_stat_token_t, j); row = rspamd_fstring_append (row, word->begin, word->len); } /* Now we need to create a new row here */ for (j = 0; j < RSPAMD_SHINGLE_SIZE; j ++) { rspamd_cryptobox_siphash ((guchar *)&val, row->str, row->len, keys[j]); g_assert (hlen > beg); hashes[j][beg] = val; } beg++; row = rspamd_fstring_assign (row, "", 0); } } } else { guint64 res[SHINGLES_WINDOW * RSPAMD_SHINGLE_SIZE], seed; switch (alg) { case RSPAMD_SHINGLES_XXHASH: ht = RSPAMD_CRYPTOBOX_XXHASH64; break; case RSPAMD_SHINGLES_MUMHASH: ht = RSPAMD_CRYPTOBOX_MUMHASH; break; default: ht = RSPAMD_CRYPTOBOX_HASHFAST_INDEPENDENT; break; } memset (res, 0, sizeof (res)); for (i = 0; i <= (gint)input->len; i ++) { if (i - beg >= SHINGLES_WINDOW || i == (gint)input->len) { for (j = 0; j < RSPAMD_SHINGLE_SIZE; j ++) { /* Shift hashes window to right */ for (k = 0; k < SHINGLES_WINDOW - 1; k ++) { res[j * SHINGLES_WINDOW + k] = res[j * SHINGLES_WINDOW + k + 1]; } word = &g_array_index (input, rspamd_stat_token_t, beg); /* Insert the last element to the pipe */ memcpy (&seed, keys[j], sizeof (seed)); res[j * SHINGLES_WINDOW + SHINGLES_WINDOW - 1] = rspamd_cryptobox_fast_hash_specific (ht, word->begin, word->len, seed); val = 0; for (k = 0; k < SHINGLES_WINDOW; k ++) { val ^= res[j * SHINGLES_WINDOW + k] >> (8 * (SHINGLES_WINDOW - k - 1)); } g_assert (hlen > beg); hashes[j][beg] = val; } beg++; } } } /* Now we need to filter all hashes and make a shingles result */ for (i = 0; i < RSPAMD_SHINGLE_SIZE; i ++) { res->hashes[i] = filter (hashes[i], hlen, i, key, filterd); g_free (hashes[i]); } g_free (hashes); rspamd_fstring_free (row); return res; }
gboolean rspamd_client_command (struct rspamd_client_connection *conn, const gchar *command, GQueue *attrs, FILE *in, rspamd_client_callback cb, gpointer ud, gboolean compressed, const gchar *comp_dictionary, GError **err) { struct rspamd_client_request *req; struct rspamd_http_client_header *nh; gchar *p; gsize remain, old_len; GList *cur; GString *input = NULL; rspamd_fstring_t *body; guint dict_id = 0; gsize dict_len = 0; void *dict = NULL; ZSTD_CCtx *zctx; req = g_slice_alloc0 (sizeof (struct rspamd_client_request)); req->conn = conn; req->cb = cb; req->ud = ud; req->msg = rspamd_http_new_message (HTTP_REQUEST); if (conn->key) { req->msg->peer_key = rspamd_pubkey_ref (conn->key); } if (in != NULL) { /* Read input stream */ input = g_string_sized_new (BUFSIZ); while (!feof (in)) { p = input->str + input->len; remain = input->allocated_len - input->len - 1; if (remain == 0) { old_len = input->len; g_string_set_size (input, old_len * 2); input->len = old_len; continue; } remain = fread (p, 1, remain, in); if (remain > 0) { input->len += remain; input->str[input->len] = '\0'; } } if (ferror (in) != 0) { g_set_error (err, RCLIENT_ERROR, ferror ( in), "input IO error: %s", strerror (ferror (in))); g_slice_free1 (sizeof (struct rspamd_client_request), req); g_string_free (input, TRUE); return FALSE; } if (!compressed) { body = rspamd_fstring_new_init (input->str, input->len); } else { if (comp_dictionary) { dict = rspamd_file_xmap (comp_dictionary, PROT_READ, &dict_len); if (dict == NULL) { g_set_error (err, RCLIENT_ERROR, errno, "cannot open dictionary %s: %s", comp_dictionary, strerror (errno)); g_slice_free1 (sizeof (struct rspamd_client_request), req); g_string_free (input, TRUE); return FALSE; } dict_id = ZDICT_getDictID (comp_dictionary, dict_len); if (dict_id == 0) { g_set_error (err, RCLIENT_ERROR, errno, "cannot open dictionary %s: %s", comp_dictionary, strerror (errno)); g_slice_free1 (sizeof (struct rspamd_client_request), req); g_string_free (input, TRUE); munmap (dict, dict_len); return FALSE; } } body = rspamd_fstring_sized_new (ZSTD_compressBound (input->len)); zctx = ZSTD_createCCtx (); body->len = ZSTD_compress_usingDict (zctx, body->str, body->allocated, input->str, input->len, dict, dict_len, 1); munmap (dict, dict_len); if (ZSTD_isError (body->len)) { g_set_error (err, RCLIENT_ERROR, ferror ( in), "compression error"); g_slice_free1 (sizeof (struct rspamd_client_request), req); g_string_free (input, TRUE); rspamd_fstring_free (body); ZSTD_freeCCtx (zctx); return FALSE; } ZSTD_freeCCtx (zctx); } rspamd_http_message_set_body_from_fstring_steal (req->msg, body); req->input = input; } else { req->input = NULL; } /* Convert headers */ cur = attrs->head; while (cur != NULL) { nh = cur->data; rspamd_http_message_add_header (req->msg, nh->name, nh->value); cur = g_list_next (cur); } if (compressed) { rspamd_http_message_add_header (req->msg, "Compression", "zstd"); if (dict_id != 0) { gchar dict_str[32]; rspamd_snprintf (dict_str, sizeof (dict_str), "%ud", dict_id); rspamd_http_message_add_header (req->msg, "Dictionary", dict_str); } } req->msg->url = rspamd_fstring_append (req->msg->url, "/", 1); req->msg->url = rspamd_fstring_append (req->msg->url, command, strlen (command)); conn->req = req; if (compressed) { rspamd_http_connection_write_message (conn->http_conn, req->msg, NULL, "application/x-compressed", req, conn->fd, &conn->timeout, conn->ev_base); } else { rspamd_http_connection_write_message (conn->http_conn, req->msg, NULL, "text/plain", req, conn->fd, &conn->timeout, conn->ev_base); } return TRUE; }