2 #include "OpenDKIM.hpp"
4 #include "iobuffer.hpp"
6 #include <stdbool.h> // needs to be above <dkim.h>
10 #include <glog/logging.h>
13 // Not nice to use "unsigned char" for character data.
14 u_char
* uc(char const* cp
)
16 return reinterpret_cast<u_char
*>(const_cast<char*>(cp
));
19 char const* c(unsigned char* ucp
) { return reinterpret_cast<char const*>(ucp
); }
21 constexpr unsigned char id_v
[]{"DKIM::verify"};
22 constexpr unsigned char id_s
[]{"DKIM::sign"};
28 : lib_(CHECK_NOTNULL(dkim_init(nullptr, nullptr)))
30 char const* signhdrs
[] = {"cc",
32 "content-transfer-encoding",
60 CHECK_EQ(dkim_options(lib_
, DKIM_OP_SETOPT
, DKIM_OPTS_SIGNHDRS
, signhdrs
,
61 sizeof(char const**)),
64 char const* oversign
[] = {"cc", "date", "from",
65 "in-reply-to", "message-id", "sender",
66 "subject", "to", nullptr};
68 CHECK_EQ(dkim_options(lib_
, DKIM_OP_SETOPT
, DKIM_OPTS_OVERSIGNHDRS
, oversign
,
69 sizeof(char const**)),
79 void lib::header(std::string_view header
)
81 if (header
.size() && header
.back() == '\n')
82 header
.remove_suffix(1);
83 if (header
.size() && header
.back() == '\r')
84 header
.remove_suffix(1);
86 CHECK_EQ((status_
= dkim_header(dkim_
, uc(header
.data()), header
.length())),
88 << "dkim_header error: " << dkim_getresultstr(status_
);
90 // LOG(INFO) << "processed: " << std::string(header.data(),
96 status_
= dkim_eoh(dkim_
);
104 LOG(WARNING
) << "dkim_eoh error: " << dkim_getresultstr(status_
);
109 void lib::body(std::string_view body
)
111 CHECK_EQ((status_
= dkim_body(dkim_
, uc(body
.data()), body
.length())),
113 << "dkim_body error: " << dkim_getresultstr(status_
);
116 void lib::chunk(std::string_view chunk
)
118 CHECK_EQ((status_
= dkim_chunk(dkim_
, uc(chunk
.data()), chunk
.length())),
120 << "dkim_chunk error: " << dkim_getresultstr(status_
);
125 status_
= dkim_eom(dkim_
, nullptr);
129 case DKIM_STAT_NOSIG
:
134 LOG(WARNING
) << "dkim_eom error: " << dkim_getresultstr(status_
);
139 //.............................................................................
141 sign::sign(char const* secretkey
,
142 char const* selector
,
147 dkim_
= dkim_sign(lib_
,
154 (typ
== body_type::binary
) ? DKIM_CANON_SIMPLE
155 : DKIM_CANON_RELAXED
,
160 CHECK_NOTNULL(dkim_
);
161 CHECK_EQ(status_
, DKIM_STAT_OK
);
164 std::string
sign::getsighdr()
166 auto const initial
{strlen(DKIM_SIGNHEADER
) + 2};
167 unsigned char* buf
= nullptr;
169 status_
= dkim_getsighdr_d(dkim_
, initial
, &buf
, &len
);
170 CHECK_EQ(status_
, DKIM_STAT_OK
);
171 return std::string(c(buf
), len
);
174 //.............................................................................
176 void verify::foreach_sig(std::function
<void(char const* domain
,
178 char const* identity
,
179 char const* selector
,
180 char const* b
)> func
)
183 DKIM_SIGINFO
** sigs
= nullptr;
184 status_
= dkim_getsiglist(dkim_
, &sigs
, &nsigs
);
185 if (status_
== DKIM_STAT_INVALID
) {
186 LOG(WARNING
) << "skipping DKIM sigs";
189 CHECK_EQ(status_
, DKIM_STAT_OK
);
191 iobuffer
<u_char
> identity(4 * 1024);
193 for (auto i
{0}; i
< nsigs
; ++i
) {
194 auto const dom
= CHECK_NOTNULL(dkim_sig_getdomain(sigs
[i
]));
196 auto const flg
= dkim_sig_getflags(sigs
[i
]);
197 if ((flg
& DKIM_SIGFLAG_IGNORE
) != 0) {
198 LOG(INFO
) << "ignoring signature for domain " << dom
;
201 if ((flg
& DKIM_SIGFLAG_TESTKEY
) != 0) {
202 LOG(INFO
) << "testkey for domain " << dom
;
205 if ((flg
& DKIM_SIGFLAG_PROCESSED
) == 0) {
206 LOG(INFO
) << "ignoring unprocessed sig for domain " << dom
;
210 auto const bh
= dkim_sig_getbh(sigs
[i
]);
211 if (bh
!= DKIM_SIGBH_MATCH
) {
212 LOG(INFO
) << "body hash mismatch for domain " << dom
;
216 status_
= dkim_sig_getkeysize(sigs
[i
], &bits
);
217 if (status_
== DKIM_STAT_OK
) {
219 LOG(WARNING
) << "keysize " << bits
<< " too small for domain " << dom
;
223 LOG(WARNING
) << "getkeysize failed for domain " << dom
<< " with "
224 << dkim_getresultstr(status_
);
228 ((flg
& DKIM_SIGFLAG_PASSED
) != 0) && (bh
== DKIM_SIGBH_MATCH
);
231 dkim_sig_getidentity(dkim_
, sigs
[i
], identity
.data(), identity
.size()),
234 auto const selector
= dkim_sig_getselector(sigs
[i
]);
236 auto const b
= dkim_sig_gettagvalue(sigs
[i
], false, uc("b"));
238 func(c(dom
), passed
, c(identity
.data()), c(selector
), c(b
));
244 dkim_
= CHECK_NOTNULL(dkim_verify(lib_
, id_v
, nullptr, &status_
));
250 DKIM_SIGINFO
** sigs
= nullptr;
251 status_
= dkim_getsiglist(dkim_
, &sigs
, &nsigs
);
252 CHECK_EQ(status_
, DKIM_STAT_OK
);
254 LOG(INFO
) << "nsigs == " << nsigs
;
256 for (auto i
{0}; i
< nsigs
; ++i
) {
257 LOG(INFO
) << i
<< " domain == " << dkim_sig_getdomain(sigs
[i
]);
258 auto flg
= dkim_sig_getflags(sigs
[i
]);
259 if ((flg
& DKIM_SIGFLAG_IGNORE
) != 0) {
260 LOG(INFO
) << "DKIM_SIGFLAG_IGNORE";
262 if ((flg
& DKIM_SIGFLAG_PROCESSED
) != 0) {
263 LOG(INFO
) << "DKIM_SIGFLAG_PROCESSED";
265 if ((flg
& DKIM_SIGFLAG_PASSED
) != 0) {
266 LOG(INFO
) << "DKIM_SIGFLAG_PASSED";
268 if ((flg
& DKIM_SIGFLAG_TESTKEY
) != 0) {
269 LOG(INFO
) << "DKIM_SIGFLAG_TESTKEY";
271 if ((flg
& DKIM_SIGFLAG_NOSUBDOMAIN
) != 0) {
272 LOG(INFO
) << "DKIM_SIGFLAG_NOSUBDOMAIN";
277 auto sig
{dkim_getsignature(dkim_
)};
280 LOG(INFO
) << "dkim_getsignature domain == " << dkim_sig_getdomain(sig
);
286 status_
= dkim_sig_getcanonlen(dkim_
, sig
, &msglen
, &canonlen
, &signlen
);
288 CHECK_EQ(status_
, DKIM_STAT_OK
);
290 LOG(INFO
) << "msglen == " << msglen
;
291 LOG(INFO
) << "canonlen == " << canonlen
;
292 LOG(INFO
) << "signlen == " << signlen
;
295 status_
= dkim_sig_getsignedhdrs(dkim_
, sig
, nullptr, 0, &nhdrs
);
296 if (status_
!= DKIM_STAT_NORESOURCE
) {
300 LOG(INFO
) << "nhdrs == " << nhdrs
;
302 auto constexpr hdr_sz
{DKIM_MAXHEADER
+ 1};
303 auto signedhdrs
{std::vector
<unsigned char>(nhdrs
* hdr_sz
, '\0')};
306 dkim_sig_getsignedhdrs(dkim_
, sig
, &signedhdrs
[0], hdr_sz
, &nhdrs
);
307 CHECK_EQ(status_
, DKIM_STAT_OK
);
309 for (auto i
{0u}; i
< nhdrs
; ++i
)
310 LOG(INFO
) << &signedhdrs
[i
* hdr_sz
];
319 bool verify::sig_syntax(std::string_view sig
)
321 return dkim_sig_syntax(dkim_
, uc(sig
.data()), sig
.length()) == DKIM_STAT_OK
;
324 } // namespace OpenDKIM