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)))
29 char const* signhdrs
[] = {"cc",
31 "content-transfer-encoding",
59 CHECK_EQ(dkim_options(lib_
, DKIM_OP_SETOPT
, DKIM_OPTS_SIGNHDRS
, signhdrs
,
60 sizeof(char const**)),
63 char const* oversign
[] = {"cc", "date", "from",
64 "in-reply-to", "message-id", "sender",
65 "subject", "to", nullptr};
67 CHECK_EQ(dkim_options(lib_
, DKIM_OP_SETOPT
, DKIM_OPTS_OVERSIGNHDRS
, oversign
,
68 sizeof(char const**)),
78 void lib::header(std::string_view header
)
80 if (header
.size() && header
.back() == '\n')
81 header
.remove_suffix(1);
82 if (header
.size() && header
.back() == '\r')
83 header
.remove_suffix(1);
85 CHECK_EQ((status_
= dkim_header(dkim_
, uc(header
.data()), header
.length())),
87 << "dkim_header error: " << dkim_getresultstr(status_
);
89 // LOG(INFO) << "processed: " << std::string(header.data(),
95 status_
= dkim_eoh(dkim_
);
103 LOG(WARNING
) << "dkim_eoh error: " << dkim_getresultstr(status_
);
108 void lib::body(std::string_view body
)
110 CHECK_EQ((status_
= dkim_body(dkim_
, uc(body
.data()), body
.length())),
112 << "dkim_body error: " << dkim_getresultstr(status_
);
115 void lib::chunk(std::string_view chunk
)
117 CHECK_EQ((status_
= dkim_chunk(dkim_
, uc(chunk
.data()), chunk
.length())),
119 << "dkim_chunk error: " << dkim_getresultstr(status_
);
124 status_
= dkim_eom(dkim_
, nullptr);
128 case DKIM_STAT_NOSIG
:
133 LOG(WARNING
) << "dkim_eom error: " << dkim_getresultstr(status_
);
138 //.............................................................................
140 sign::sign(char const* secretkey
,
141 char const* selector
,
146 dkim_
= dkim_sign(lib_
,
153 (typ
== body_type::binary
) ? DKIM_CANON_SIMPLE
154 : DKIM_CANON_RELAXED
,
159 CHECK_NOTNULL(dkim_
);
160 CHECK_EQ(status_
, DKIM_STAT_OK
);
163 std::string
sign::getsighdr()
165 auto const initial
{strlen(DKIM_SIGNHEADER
) + 2};
166 unsigned char* buf
= nullptr;
168 status_
= dkim_getsighdr_d(dkim_
, initial
, &buf
, &len
);
169 CHECK_EQ(status_
, DKIM_STAT_OK
);
170 return std::string(c(buf
), len
);
173 //.............................................................................
175 void verify::foreach_sig(std::function
<void(char const* domain
,
177 char const* identity
,
178 char const* selector
,
179 char const* b
)> func
)
182 DKIM_SIGINFO
** sigs
= nullptr;
183 status_
= dkim_getsiglist(dkim_
, &sigs
, &nsigs
);
184 if (status_
== DKIM_STAT_INVALID
) {
185 LOG(WARNING
) << "skipping DKIM sigs";
188 CHECK_EQ(status_
, DKIM_STAT_OK
);
190 iobuffer
<u_char
> identity(4 * 1024);
192 for (auto i
{0}; i
< nsigs
; ++i
) {
193 auto const dom
= CHECK_NOTNULL(dkim_sig_getdomain(sigs
[i
]));
195 auto const flg
= dkim_sig_getflags(sigs
[i
]);
196 if ((flg
& DKIM_SIGFLAG_IGNORE
) != 0) {
197 LOG(INFO
) << "ignoring signature for domain " << dom
;
200 if ((flg
& DKIM_SIGFLAG_TESTKEY
) != 0) {
201 LOG(INFO
) << "testkey for domain " << dom
;
204 if ((flg
& DKIM_SIGFLAG_PROCESSED
) == 0) {
205 LOG(INFO
) << "ignoring unprocessed sig for domain " << dom
;
209 auto const bh
= dkim_sig_getbh(sigs
[i
]);
210 if (bh
!= DKIM_SIGBH_MATCH
) {
211 LOG(INFO
) << "body hash mismatch for domain " << dom
;
215 status_
= dkim_sig_getkeysize(sigs
[i
], &bits
);
216 if (status_
== DKIM_STAT_OK
) {
218 LOG(WARNING
) << "keysize " << bits
<< " too small for domain " << dom
;
222 LOG(WARNING
) << "getkeysize failed for domain " << dom
<< " with "
223 << dkim_getresultstr(status_
);
227 ((flg
& DKIM_SIGFLAG_PASSED
) != 0) && (bh
== DKIM_SIGBH_MATCH
);
230 dkim_sig_getidentity(dkim_
, sigs
[i
], identity
.data(), identity
.size()),
233 auto const selector
= dkim_sig_getselector(sigs
[i
]);
235 auto const b
= dkim_sig_gettagvalue(sigs
[i
], false, uc("b"));
237 func(c(dom
), passed
, c(identity
.data()), c(selector
), c(b
));
243 dkim_
= CHECK_NOTNULL(dkim_verify(lib_
, id_v
, nullptr, &status_
));
249 DKIM_SIGINFO
** sigs
= nullptr;
250 status_
= dkim_getsiglist(dkim_
, &sigs
, &nsigs
);
251 CHECK_EQ(status_
, DKIM_STAT_OK
);
253 LOG(INFO
) << "nsigs == " << nsigs
;
255 for (auto i
{0}; i
< nsigs
; ++i
) {
256 LOG(INFO
) << i
<< " domain == " << dkim_sig_getdomain(sigs
[i
]);
257 auto flg
= dkim_sig_getflags(sigs
[i
]);
258 if ((flg
& DKIM_SIGFLAG_IGNORE
) != 0) {
259 LOG(INFO
) << "DKIM_SIGFLAG_IGNORE";
261 if ((flg
& DKIM_SIGFLAG_PROCESSED
) != 0) {
262 LOG(INFO
) << "DKIM_SIGFLAG_PROCESSED";
264 if ((flg
& DKIM_SIGFLAG_PASSED
) != 0) {
265 LOG(INFO
) << "DKIM_SIGFLAG_PASSED";
267 if ((flg
& DKIM_SIGFLAG_TESTKEY
) != 0) {
268 LOG(INFO
) << "DKIM_SIGFLAG_TESTKEY";
270 if ((flg
& DKIM_SIGFLAG_NOSUBDOMAIN
) != 0) {
271 LOG(INFO
) << "DKIM_SIGFLAG_NOSUBDOMAIN";
276 auto sig
{dkim_getsignature(dkim_
)};
279 LOG(INFO
) << "dkim_getsignature domain == " << dkim_sig_getdomain(sig
);
285 status_
= dkim_sig_getcanonlen(dkim_
, sig
, &msglen
, &canonlen
, &signlen
);
287 CHECK_EQ(status_
, DKIM_STAT_OK
);
289 LOG(INFO
) << "msglen == " << msglen
;
290 LOG(INFO
) << "canonlen == " << canonlen
;
291 LOG(INFO
) << "signlen == " << signlen
;
294 status_
= dkim_sig_getsignedhdrs(dkim_
, sig
, nullptr, 0, &nhdrs
);
295 if (status_
!= DKIM_STAT_NORESOURCE
) {
299 LOG(INFO
) << "nhdrs == " << nhdrs
;
301 auto constexpr hdr_sz
{DKIM_MAXHEADER
+ 1};
302 auto signedhdrs
{std::vector
<unsigned char>(nhdrs
* hdr_sz
, '\0')};
305 dkim_sig_getsignedhdrs(dkim_
, sig
, &signedhdrs
[0], hdr_sz
, &nhdrs
);
306 CHECK_EQ(status_
, DKIM_STAT_OK
);
308 for (auto i
{0u}; i
< nhdrs
; ++i
)
309 LOG(INFO
) << &signedhdrs
[i
* hdr_sz
];
318 bool verify::sig_syntax(std::string_view sig
)
320 return dkim_sig_syntax(dkim_
, uc(sig
.data()), sig
.length()) == DKIM_STAT_OK
;
323 } // namespace OpenDKIM