1--[[ 2Copyright (c) 2019, Vsevolod Stakhov <vsevolod@highsecure.ru> 3 4Licensed under the Apache License, Version 2.0 (the "License"); 5you may not use this file except in compliance with the License. 6You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10Unless required by applicable law or agreed to in writing, software 11distributed under the License is distributed on an "AS IS" BASIS, 12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13See the License for the specific language governing permissions and 14limitations under the License. 15]]-- 16 17--[[[ 18-- @module lua_ffi/dkim 19-- This module contains ffi interfaces to DKIM 20--]] 21 22local ffi = require 'ffi' 23 24ffi.cdef[[ 25struct rspamd_dkim_sign_context_s; 26struct rspamd_dkim_key_s; 27struct rspamd_task; 28enum rspamd_dkim_key_format { 29 RSPAMD_DKIM_KEY_FILE = 0, 30 RSPAMD_DKIM_KEY_PEM, 31 RSPAMD_DKIM_KEY_BASE64, 32 RSPAMD_DKIM_KEY_RAW, 33}; 34enum rspamd_dkim_type { 35 RSPAMD_DKIM_NORMAL, 36 RSPAMD_DKIM_ARC_SIG, 37 RSPAMD_DKIM_ARC_SEAL 38}; 39struct rspamd_dkim_sign_context_s* 40rspamd_create_dkim_sign_context (struct rspamd_task *task, 41 struct rspamd_dkim_key_s *priv_key, 42 int headers_canon, 43 int body_canon, 44 const char *dkim_headers, 45 enum rspamd_dkim_type type, 46 void *unused); 47struct rspamd_dkim_key_s* rspamd_dkim_sign_key_load (const char *what, size_t len, 48 enum rspamd_dkim_key_format, 49 void *err); 50void rspamd_dkim_key_unref (struct rspamd_dkim_key_s *k); 51struct GString *rspamd_dkim_sign (struct rspamd_task *task, 52 const char *selector, 53 const char *domain, 54 unsigned long expire, 55 size_t len, 56 unsigned int idx, 57 const char *arc_cv, 58 struct rspamd_dkim_sign_context_s *ctx); 59]] 60 61local function load_sign_key(what, format) 62 if not format then 63 format = ffi.C.RSPAMD_DKIM_KEY_PEM 64 else 65 if format == 'file' then 66 format = ffi.C.RSPAMD_DKIM_KEY_FILE 67 elseif format == 'base64' then 68 format = ffi.C.RSPAMD_DKIM_KEY_BASE64 69 elseif format == 'raw' then 70 format = ffi.C.RSPAMD_DKIM_KEY_RAW 71 else 72 return nil,'unknown key format' 73 end 74 end 75 76 return ffi.C.rspamd_dkim_sign_key_load(what, #what, format, nil) 77end 78 79local default_dkim_headers = 80"(o)from:(o)sender:(o)reply-to:(o)subject:(o)date:(o)message-id:" .. 81"(o)to:(o)cc:(o)mime-version:(o)content-type:(o)content-transfer-encoding:" .. 82"resent-to:resent-cc:resent-from:resent-sender:resent-message-id:" .. 83"(o)in-reply-to:(o)references:list-id:list-owner:list-unsubscribe:" .. 84"list-subscribe:list-post:(o)openpgp:(o)autocrypt" 85 86local function create_sign_context(task, privkey, dkim_headers, sign_type) 87 if not task or not privkey then 88 return nil,'invalid arguments' 89 end 90 91 if not dkim_headers then 92 dkim_headers = default_dkim_headers 93 end 94 95 if not sign_type then 96 sign_type = 'dkim' 97 end 98 99 if sign_type == 'dkim' then 100 sign_type = ffi.C.RSPAMD_DKIM_NORMAL 101 elseif sign_type == 'arc-sig' then 102 sign_type = ffi.C.RSPAMD_DKIM_ARC_SIG 103 elseif sign_type == 'arc-seal' then 104 sign_type = ffi.C.RSPAMD_DKIM_ARC_SEAL 105 else 106 return nil,'invalid sign type' 107 end 108 109 110 return ffi.C.rspamd_create_dkim_sign_context(task:topointer(), privkey, 111 1, 1, dkim_headers, sign_type, nil) 112end 113 114local function do_sign(task, sign_context, selector, domain, 115 expire, len, arc_idx) 116 if not task or not sign_context or not selector or not domain then 117 return nil,'invalid arguments' 118 end 119 120 if not expire then expire = 0 end 121 if not len then len = 0 end 122 if not arc_idx then arc_idx = 0 end 123 124 local gstring = ffi.C.rspamd_dkim_sign(task:topointer(), selector, domain, expire, len, arc_idx, nil, sign_context) 125 126 if not gstring then 127 return nil,'cannot sign' 128 end 129 130 local ret = ffi.string(gstring.str, gstring.len) 131 ffi.C.g_string_free(gstring, true) 132 133 return ret 134end 135 136return { 137 load_sign_key = load_sign_key, 138 create_sign_context = create_sign_context, 139 do_sign = do_sign 140} 141