/*- * Copyright 2016 Vsevolod Stakhov * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef DKIM_H_ #define DKIM_H_ #include "config.h" #include "contrib/libev/ev.h" #include "dns.h" #include "ref.h" /* Main types and definitions */ #define RSPAMD_DKIM_SIGNHEADER "DKIM-Signature" #define RSPAMD_DKIM_ARC_SIGNHEADER "ARC-Message-Signature" #define RSPAMD_DKIM_ARC_AUTHHEADER "ARC-Authentication-Results" #define RSPAMD_DKIM_ARC_SEALHEADER "ARC-Seal" /* DKIM signature header */ /* Errors (from OpenDKIM) */ #define DKIM_SIGERROR_UNKNOWN (-1) /* unknown error */ #define DKIM_SIGERROR_VERSION 1 /* unsupported version */ #define DKIM_SIGERROR_EXPIRED 3 /* signature expired */ #define DKIM_SIGERROR_FUTURE 4 /* signature in the future */ #define DKIM_SIGERROR_NOREC 6 /* No record */ #define DKIM_SIGERROR_INVALID_HC 7 /* c= invalid (header) */ #define DKIM_SIGERROR_INVALID_BC 8 /* c= invalid (body) */ #define DKIM_SIGERROR_INVALID_A 10 /* a= invalid */ #define DKIM_SIGERROR_INVALID_L 12 /* l= invalid */ #define DKIM_SIGERROR_EMPTY_D 16 /* d= empty */ #define DKIM_SIGERROR_EMPTY_S 18 /* s= empty */ #define DKIM_SIGERROR_EMPTY_B 20 /* b= empty */ #define DKIM_SIGERROR_NOKEY 22 /* no key found in DNS */ #define DKIM_SIGERROR_KEYFAIL 24 /* DNS query failed */ #define DKIM_SIGERROR_EMPTY_BH 26 /* bh= empty */ #define DKIM_SIGERROR_BADSIG 28 /* signature mismatch */ #define DKIM_SIGERROR_EMPTY_H 31 /* h= empty */ #define DKIM_SIGERROR_INVALID_H 32 /* h= missing req'd entries */ #define DKIM_SIGERROR_KEYHASHMISMATCH 37 /* sig-key hash mismatch */ #define DKIM_SIGERROR_EMPTY_V 45 /* v= tag empty */ #ifdef __cplusplus extern "C" { #endif /* Check results */ enum rspamd_dkim_check_rcode { DKIM_CONTINUE = 0, DKIM_REJECT, DKIM_TRYAGAIN, DKIM_NOTFOUND, DKIM_RECORD_ERROR, DKIM_PERM_ERROR, }; #define DKIM_CANON_SIMPLE 0 /* as specified in DKIM spec */ #define DKIM_CANON_RELAXED 1 /* as specified in DKIM spec */ struct rspamd_dkim_context_s; typedef struct rspamd_dkim_context_s rspamd_dkim_context_t; struct rspamd_dkim_sign_context_s; typedef struct rspamd_dkim_sign_context_s rspamd_dkim_sign_context_t; struct rspamd_dkim_key_s; typedef struct rspamd_dkim_key_s rspamd_dkim_key_t; typedef struct rspamd_dkim_key_s rspamd_dkim_sign_key_t; struct rspamd_task; enum rspamd_dkim_key_format { RSPAMD_DKIM_KEY_FILE = 0, RSPAMD_DKIM_KEY_PEM, RSPAMD_DKIM_KEY_BASE64, RSPAMD_DKIM_KEY_RAW, RSPAMD_DKIM_KEY_UNKNOWN }; enum rspamd_dkim_type { RSPAMD_DKIM_NORMAL, RSPAMD_DKIM_ARC_SIG, RSPAMD_DKIM_ARC_SEAL }; /* Signature methods */ enum rspamd_sign_type { DKIM_SIGN_UNKNOWN = -2, DKIM_SIGN_RSASHA1 = 0, DKIM_SIGN_RSASHA256, DKIM_SIGN_RSASHA512, DKIM_SIGN_ECDSASHA256, DKIM_SIGN_ECDSASHA512, DKIM_SIGN_EDDSASHA256, }; enum rspamd_dkim_key_type { RSPAMD_DKIM_KEY_RSA = 0, RSPAMD_DKIM_KEY_ECDSA, RSPAMD_DKIM_KEY_EDDSA }; struct rspamd_dkim_check_result { enum rspamd_dkim_check_rcode rcode; rspamd_dkim_context_t *ctx; /* Processed parts */ const char *selector; const char *domain; const char *short_b; const char *fail_reason; }; /* Err MUST be freed if it is not NULL, key is allocated by slice allocator */ typedef void (*dkim_key_handler_f)(rspamd_dkim_key_t *key, gsize keylen, rspamd_dkim_context_t *ctx, gpointer ud, GError *err); /** * Create new dkim context from signature * @param sig message's signature * @param pool pool to allocate memory from * @param time_jitter jitter in seconds to allow time diff while checking * @param err pointer to error object * @return new context or NULL */ rspamd_dkim_context_t *rspamd_create_dkim_context(const char *sig, rspamd_mempool_t *pool, struct rspamd_dns_resolver *resolver, unsigned int time_jitter, enum rspamd_dkim_type type, GError **err); /** * Create new dkim context for making a signature * @param task * @param priv_key * @param err * @return */ rspamd_dkim_sign_context_t *rspamd_create_dkim_sign_context(struct rspamd_task *task, rspamd_dkim_sign_key_t *priv_key, int headers_canon, int body_canon, const char *dkim_headers, enum rspamd_dkim_type type, GError **err); /** * Load dkim key * @param path * @param err * @return */ rspamd_dkim_sign_key_t *rspamd_dkim_sign_key_load(const char *what, gsize len, enum rspamd_dkim_key_format type, GError **err); /** * Invalidate modified sign key * @param key * @return */ gboolean rspamd_dkim_sign_key_maybe_invalidate(rspamd_dkim_sign_key_t *key, time_t mtime); /** * Make DNS request for specified context and obtain and parse key * @param ctx dkim context from signature * @param resolver dns resolver object * @param s async session to make request * @return */ gboolean rspamd_get_dkim_key(rspamd_dkim_context_t *ctx, struct rspamd_task *task, dkim_key_handler_f handler, gpointer ud); /** * Check task for dkim context using dkim key * @param ctx dkim verify context * @param key dkim key (from cache or from dns request) * @param task task to check * @return */ struct rspamd_dkim_check_result *rspamd_dkim_check(rspamd_dkim_context_t *ctx, rspamd_dkim_key_t *key, struct rspamd_task *task); struct rspamd_dkim_check_result * rspamd_dkim_create_result(rspamd_dkim_context_t *ctx, enum rspamd_dkim_check_rcode rcode, struct rspamd_task *task); GString *rspamd_dkim_sign(struct rspamd_task *task, const char *selector, const char *domain, time_t expire, gsize len, unsigned int idx, const char *arc_cv, rspamd_dkim_sign_context_t *ctx); rspamd_dkim_key_t *rspamd_dkim_key_ref(rspamd_dkim_key_t *k); void rspamd_dkim_key_unref(rspamd_dkim_key_t *k); rspamd_dkim_sign_key_t *rspamd_dkim_sign_key_ref(rspamd_dkim_sign_key_t *k); void rspamd_dkim_sign_key_unref(rspamd_dkim_sign_key_t *k); const char *rspamd_dkim_get_domain(rspamd_dkim_context_t *ctx); const char *rspamd_dkim_get_selector(rspamd_dkim_context_t *ctx); const char *rspamd_dkim_get_dns_key(rspamd_dkim_context_t *ctx); unsigned int rspamd_dkim_key_get_ttl(rspamd_dkim_key_t *k); /** * Create DKIM public key from a raw data * @param keydata * @param keylen * @param type * @param err * @return */ rspamd_dkim_key_t *rspamd_dkim_make_key(const char *keydata, unsigned int keylen, enum rspamd_dkim_key_type type, GError **err); #define RSPAMD_DKIM_KEY_ID_LEN 16 /** * Returns key id for dkim key (raw md5 of RSPAMD_DKIM_KEY_ID_LEN) * NOT ZERO TERMINATED, use RSPAMD_DKIM_KEY_ID_LEN for length * @param key * @return */ const unsigned char *rspamd_dkim_key_id(rspamd_dkim_key_t *key); /** * Parse DKIM public key from a TXT record * @param txt * @param keylen * @param err * @return */ rspamd_dkim_key_t *rspamd_dkim_parse_key(const char *txt, gsize *keylen, GError **err); /** * Canonicalise header using relaxed algorithm * @param hname * @param hvalue * @param out * @param outlen * @return */ goffset rspamd_dkim_canonize_header_relaxed_str(const char *hname, const char *hvalue, char *out, gsize outlen); /** * Checks public and private keys for match * @param pk * @param sk * @param err * @return */ gboolean rspamd_dkim_match_keys(rspamd_dkim_key_t *pk, rspamd_dkim_sign_key_t *sk, GError **err); /** * Free DKIM key * @param key */ void rspamd_dkim_key_free(rspamd_dkim_key_t *key); #ifdef __cplusplus } #endif #endif /* DKIM_H_ */