1 #include "OpenDKIM.hpp"
3 #include "iobuffer.hpp"
5 #include <stdbool.h> // needs to be above <dkim.h>
9 #include <glog/logging.h>
12 // Not nice to use "unsigned char" for character data.
13 u_char
* uc(char const* cp
)
15 return reinterpret_cast<u_char
*>(const_cast<char*>(cp
));
18 char const* c(unsigned char* ucp
) { return reinterpret_cast<char const*>(ucp
); }
20 constexpr unsigned char id_v
[]{"DKIM::verify"};
21 constexpr unsigned char id_s
[]{"DKIM::sign"};
27 : lib_(CHECK_NOTNULL(dkim_init(nullptr, nullptr)))
37 void lib::header(std::string_view header
)
39 if (header
.size() && header
.back() == '\n')
40 header
.remove_suffix(1);
41 if (header
.size() && header
.back() == '\r')
42 header
.remove_suffix(1);
44 CHECK_EQ((status_
= dkim_header(dkim_
, uc(header
.data()), header
.length())),
46 << "dkim_header error: " << dkim_getresultstr(status_
);
48 // LOG(INFO) << "processed: " << std::string(header.data(),
54 status_
= dkim_eoh(dkim_
);
62 LOG(WARNING
) << "dkim_eoh error: " << dkim_getresultstr(status_
);
67 void lib::body(std::string_view body
)
69 CHECK_EQ((status_
= dkim_body(dkim_
, uc(body
.data()), body
.length())),
71 << "dkim_body error: " << dkim_getresultstr(status_
);
74 void lib::chunk(std::string_view chunk
)
76 CHECK_EQ((status_
= dkim_chunk(dkim_
, uc(chunk
.data()), chunk
.length())),
78 << "dkim_chunk error: " << dkim_getresultstr(status_
);
83 status_
= dkim_eom(dkim_
, nullptr);
92 LOG(WARNING
) << "dkim_eom error: " << dkim_getresultstr(status_
);
97 //.............................................................................
99 sign::sign(char const* secretkey
,
100 char const* selector
,
105 dkim_
= dkim_sign(lib_
,
112 (typ
== body_type::binary
) ? DKIM_CANON_SIMPLE
113 : DKIM_CANON_RELAXED
,
118 CHECK_NOTNULL(dkim_
);
119 CHECK_EQ(status_
, DKIM_STAT_OK
);
121 char const* hdrlist
[] = {"cc",
123 "content-transfer-encoding",
149 dkim_signhdrs(dkim_
, hdrlist
);
152 std::string
sign::getsighdr()
154 auto const initial
{strlen(DKIM_SIGNHEADER
) + 2};
155 unsigned char* buf
= nullptr;
157 status_
= dkim_getsighdr_d(dkim_
, initial
, &buf
, &len
);
158 return std::string(c(buf
), len
);
161 //.............................................................................
163 void verify::foreach_sig(std::function
<void(char const* domain
,
165 char const* identity
,
166 char const* selector
,
167 char const* b
)> func
)
170 DKIM_SIGINFO
** sigs
= nullptr;
171 status_
= dkim_getsiglist(dkim_
, &sigs
, &nsigs
);
172 if (status_
== DKIM_STAT_INVALID
) {
173 LOG(WARNING
) << "skipping DKIM sigs";
176 CHECK_EQ(status_
, DKIM_STAT_OK
);
178 iobuffer
<u_char
> identity(4 * 1024);
180 for (auto i
{0}; i
< nsigs
; ++i
) {
181 auto const dom
= CHECK_NOTNULL(dkim_sig_getdomain(sigs
[i
]));
183 auto const flg
= dkim_sig_getflags(sigs
[i
]);
184 if ((flg
& DKIM_SIGFLAG_IGNORE
) != 0) {
185 LOG(INFO
) << "ignoring signature for domain " << dom
;
188 if ((flg
& DKIM_SIGFLAG_TESTKEY
) != 0) {
189 LOG(INFO
) << "testkey for domain " << dom
;
192 if ((flg
& DKIM_SIGFLAG_PROCESSED
) == 0) {
193 LOG(INFO
) << "ignoring unprocessed sig for domain " << dom
;
197 auto const bh
= dkim_sig_getbh(sigs
[i
]);
198 if (bh
!= DKIM_SIGBH_MATCH
) {
199 LOG(INFO
) << "body hash mismatch for domain " << dom
;
203 status_
= dkim_sig_getkeysize(sigs
[i
], &bits
);
204 if (status_
== DKIM_STAT_OK
) {
206 LOG(WARNING
) << "keysize " << bits
<< " too small for domain " << dom
;
210 LOG(WARNING
) << "getkeysize failed for domain " << dom
<< " with "
211 << dkim_getresultstr(status_
);
215 ((flg
& DKIM_SIGFLAG_PASSED
) != 0) && (bh
== DKIM_SIGBH_MATCH
);
218 dkim_sig_getidentity(dkim_
, sigs
[i
], identity
.data(), identity
.size()),
221 auto const selector
= dkim_sig_getselector(sigs
[i
]);
223 auto const b
= dkim_sig_gettagvalue(sigs
[i
], false, uc("b"));
225 func(c(dom
), passed
, c(identity
.data()), c(selector
), c(b
));
231 dkim_
= CHECK_NOTNULL(dkim_verify(lib_
, id_v
, nullptr, &status_
));
237 DKIM_SIGINFO
** sigs
= nullptr;
238 status_
= dkim_getsiglist(dkim_
, &sigs
, &nsigs
);
239 CHECK_EQ(status_
, DKIM_STAT_OK
);
241 LOG(INFO
) << "nsigs == " << nsigs
;
243 for (auto i
{0}; i
< nsigs
; ++i
) {
244 LOG(INFO
) << i
<< " domain == " << dkim_sig_getdomain(sigs
[i
]);
245 auto flg
= dkim_sig_getflags(sigs
[i
]);
246 if ((flg
& DKIM_SIGFLAG_IGNORE
) != 0) {
247 LOG(INFO
) << "DKIM_SIGFLAG_IGNORE";
249 if ((flg
& DKIM_SIGFLAG_PROCESSED
) != 0) {
250 LOG(INFO
) << "DKIM_SIGFLAG_PROCESSED";
252 if ((flg
& DKIM_SIGFLAG_PASSED
) != 0) {
253 LOG(INFO
) << "DKIM_SIGFLAG_PASSED";
255 if ((flg
& DKIM_SIGFLAG_TESTKEY
) != 0) {
256 LOG(INFO
) << "DKIM_SIGFLAG_TESTKEY";
258 if ((flg
& DKIM_SIGFLAG_NOSUBDOMAIN
) != 0) {
259 LOG(INFO
) << "DKIM_SIGFLAG_NOSUBDOMAIN";
264 auto sig
{dkim_getsignature(dkim_
)};
267 LOG(INFO
) << "dkim_getsignature domain == " << dkim_sig_getdomain(sig
);
273 status_
= dkim_sig_getcanonlen(dkim_
, sig
, &msglen
, &canonlen
, &signlen
);
275 CHECK_EQ(status_
, DKIM_STAT_OK
);
277 LOG(INFO
) << "msglen == " << msglen
;
278 LOG(INFO
) << "canonlen == " << canonlen
;
279 LOG(INFO
) << "signlen == " << signlen
;
282 status_
= dkim_sig_getsignedhdrs(dkim_
, sig
, nullptr, 0, &nhdrs
);
283 if (status_
!= DKIM_STAT_NORESOURCE
) {
287 LOG(INFO
) << "nhdrs == " << nhdrs
;
289 auto constexpr hdr_sz
{DKIM_MAXHEADER
+ 1};
290 auto signedhdrs
{std::vector
<unsigned char>(nhdrs
* hdr_sz
, '\0')};
293 dkim_sig_getsignedhdrs(dkim_
, sig
, &signedhdrs
[0], hdr_sz
, &nhdrs
);
294 CHECK_EQ(status_
, DKIM_STAT_OK
);
296 for (auto i
{0u}; i
< nhdrs
; ++i
)
297 LOG(INFO
) << &signedhdrs
[i
* hdr_sz
];
306 bool verify::sig_syntax(std::string_view sig
)
308 return dkim_sig_syntax(dkim_
, uc(sig
.data()), sig
.length()) == DKIM_STAT_OK
;
311 } // namespace OpenDKIM