s/qmail 4.2.29a
Next generation secure email transport
Loading...
Searching...
No Matches
qmail-dksign.c
Go to the documentation of this file.
1#include <sys/types.h>
2#include <sys/socket.h>
3#include <sys/stat.h>
4#include <netinet/in.h>
5#include <arpa/inet.h>
6#include <unistd.h>
7#include "sig.h"
8#include "stralloc.h"
9#include "buffer.h"
10#include "error.h"
11#include "auto_qmail.h"
12#include "control.h"
13#include "str.h"
14#include "exit.h"
15#include "case.h"
16#include "constmap.h"
17#include "uint_t.h"
18#include "fd.h"
19#include "logmsg.h"
20#include "open.h"
21#include "fmt.h"
22#include "fmtqfn.h"
23#include "readwrite.h"
24#include "qmail.h"
25#include "wait.h"
26#include "pathexec.h"
27#include "rcpthosts.h"
28
29#define WHO "qmail-dksign"
30
31#define DOMAINKEYS "ssl/domainkeys/"
32
56char bufin[1000]; // RFC 5322: 998 chars - why?
57buffer bi = BUFFER_INIT(read,0,bufin,sizeof(bufin));
58char bufout[1000];
59buffer bo = BUFFER_INIT(write,1,bufout,sizeof(bufout));
60
61void die(int e) { _exit(e); }
62void die_write(char *fn) { unlink(fn); die(53); };
63void die_read() { die(54); };
64void out(char *s) { if (buffer_puts(&bo,s) == -1) _exit(111); }
65void zero() { if (buffer_put(&bo,"\0",1) == -1) _exit(111); }
66void zerodie() { zero(); buffer_flush(&bo); _exit(111); }
67
68stralloc fndkin = {0};
69stralloc fndkout = {0};
70
71stralloc sender = {0}; // will be re-written
72stralloc senddomain = {0};
73stralloc originator = {0};
74stralloc dkimdomains = {0};
76
77stralloc ecckey = {0};
78stralloc rsakey = {0};
79char *dkimparams = 0;
80
82{
83 out("ZOut of memory. (#4.3.0)\n");
84 zerodie();
85}
87{
88 out("ZUnable to switch to target directory. (#4.3.0)\n");
89 zerodie();
90}
92{
93 out("ZUnable to create DKIM stage file: ");
94 out(error_str(errno));
95 out(fndkin.s); out(". (#4.3.0)\n");
96 zerodie();
97}
99{
100 out("ZUnable to unlink DKIM stage file. (#4.3.0)\n");
101 zerodie();
102}
104{
105 out("ZUnable to read DKIM control files. (#4.3.0)\n");
106 zerodie();
107}
109{
110 out("Zqmail-dksign was invoked improperly. (#5.3.5)\n");
111 zerodie();
112}
114{
115 out("DUnable to read message for DKIM signing. (#4.3.0)\n");
116 zerodie();
117}
119{
120 out("DCan't read sign key: ");
121 out(rsakey.s);
122 out(" or ");
123 out(ecckey.s);
124 out(". (#4.3.0)\n");
125 zerodie();
126}
127
129{
130 int i;
131 stralloc domname = {0};
132
133 if (control_init() == -1) temp_control();
134
135 switch (control_readfile(&dkimdomains,"control/dkimdomains",0)) {
136 case -1: return 0;
137 case 0: if (!constmap_init(&mapdkimdomains,"",0,1)) temp_nomem(); break;
138 case 1: if (!constmap_init(&mapdkimdomains,dkimdomains.s,dkimdomains.len,1)) temp_nomem(); break;
139 }
140
141/* Check for disabled DKIM send domains */
142
143 if (!stralloc_copys(&domname,"!")) temp_nomem();
144 if (!stralloc_cats(&domname,senddomain.s)) temp_nomem();
145 if (constmap(&mapdkimdomains,domname.s,domname.len)) return 0;
146
147/* Parenting domains; senddomain 0-terminated; lowercase */
148
149 for (i = 0; i <= senddomain.len; ++i) {
150 if ((i == 0) || (senddomain.s[i] == '.'))
151 if ((dkimparams = constmap(&mapdkimdomains,senddomain.s + i,senddomain.len - i - 1))) {
153 if (!stralloc_0(&sender)) temp_nomem();
154 return 3;
155 }
156 }
157
158/* We sign only senddomains we take responsibility for: rcpthosts */
159
160 if ((dkimparams = constmap(&mapdkimdomains,"=",1))) {
161 if (rcpthosts_init() == -1) temp_control();
162 if (rcpthosts(originator.s,originator.len)) {
163 if ((control_readline(&sender,"control/defaultdomain") != 1))
164 if (control_readline(&sender,"control/me") == -1) temp_control();
165 if (!stralloc_0(&sender)) temp_nomem();
166 return 2;
167 }
168 }
169
170/* Default settings for MTA: 'defaultdomain' or even 'me' */
171
172 if ((dkimparams = constmap(&mapdkimdomains,"*",1))) {
173 if ((control_readline(&sender,"control/defaultdomain") != 1))
174 if (control_readline(&sender,"control/me") == -1) temp_control();
175 if (!stralloc_0(&sender)) temp_nomem();
176 return 1;
177 }
178
179 return 0;
180}
181
182void fnmake_dkim(unsigned long id)
183{
184 fndkin.len = fmtqfn(fndkin.s,"queue/dkim/",id,1);
185 id += id;
186 fndkout.len = fmtqfn(fndkout.s,"queue/dkim/",id,1);
187}
188
190{
191 if (unlink(fndkin.s) == -1)
192 if (errno != ENOENT) temp_unlink();
193 if (unlink(fndkout.s) == -1)
194 if (errno != ENOENT) temp_unlink();
195}
196
198{
199 int r;
200 int fd;
201 char ch;
202 struct stat st;
203
204 if (!stralloc_ready(&fndkin,FMTQFN)) temp_nomem();
205 if (!stralloc_ready(&fndkout,FMTQFN)) temp_nomem();
206
207 fnmake_dkim(getpid()); // pre-staging
208 dkim_unlink(); // duplicate, left over file
209 fd = open_excl(fndkin.s);
210 if (fd == -1) die_write(fndkin.s);
211
212 buffer_init(&bi,read,0,bufin,sizeof(bufin));
213 buffer_init(&bo,write,fd,bufout,sizeof(bufout));
214
215 for (;;) {
216 r = buffer_get(&bi,&ch,1);
217 if (r == 0) break;
218 if (r == -1) temp_read();
219 if (ch == '\r') continue;
220
221 while (ch != '\n') {
222 buffer_put(&bo,&ch,1);
223 r = buffer_get(&bi,&ch,1);
224 if (r == -1) temp_read();
225 }
226 buffer_put(&bo,"\r\n",2);
227 }
228
229 if (buffer_flush(&bo) == -1) die(51);
230 if (fstat(fd,&st) == -1) die_read();
231 if (fsync(fd) == -1) die_write(fndkin.s);
232 if (close(fd) == -1) die_write(fndkin.s);
233}
234
235/* to construct DKIM information */
236
237stralloc selector = {0};
238stralloc selectore = {0};
239stralloc sdid = {0};
240stralloc auid = {0};
241stralloc expire = {0};
242stralloc canon = {0}; // -c r = relax, s = simple, t = relaxed/simple, u = simple/realxed
243stralloc hash = {0}; // -z 1/2/3/4/5 sha1/sha2/both/ed25519/ed25519+rsa-sha256
244stralloc length = {0}; // -l
245
264int dkim_sign(const char *rsakeyfile,const char *ecckeyfile,const char *fnin,const char *fnout)
265{
266 int child;
267 int wstat;
268 char *(args[17]);
269 int i = 0;
270
271 args[i] = "qmail-dkim"; ++i;
272 args[i] = "-s"; ++i;
273 args[i] = "-q"; ++i;
274 if (sdid.len > 3) { args[i] = sdid.s; ++i; }
275 if (selector.len > 3) { args[i] = selector.s; ++i; }
276 if (selectore.len > 3) { args[i] = selectore.s; ++i; }
277 if (auid.len > 3) { args[i] = auid.s; ++i; }
278 if (expire.len > 3) { args[i] = expire.s; ++i; }
279 if (canon.len > 2) { args[i] = canon.s; ++i; }
280 if (hash.len > 2) { args[i] = hash.s; ++i; }
281 if (length.len > 2) { args[i] = length.s; ++i; }
282 args[i] = fnin; ++i;
283 args[i] = rsakeyfile; ++i;
284 args[i] = fnout; ++i;
285 if (str_len(ecckeyfile) > 3) { args[i] = ecckeyfile; ++i; }
286 args[i] = 0;
287
288 if (!(child = vfork())) {
289 pathexec(args);
290 if (errno) _exit(111);
291 _exit(100);
292 }
293
294 wait_pid(&wstat,child);
295 if (wait_crashed(wstat)) return 1;
296
297 switch (wait_exitcode(wstat)) {
298 case 1: return 1;
299 default: return 0;
300 }
301}
302
303int qmail_remote(char **qargs,int fd)
304{
305 int child;
306 int wstat;
307 char *(args[5]);
308
309 args[0] = "qmail-remote";
310 args[1] = qargs[1];
311 args[2] = qargs[2];
312 args[3] = qargs[3];
313 args[4] = 0;
314
315 if (!(child = vfork())) {
316 if (fd) {
317 if (fd_move(0,fd) == -1) _exit(111);
318 if (fd_copy(2,1) == -1) _exit(111);
319 }
320 pathexec(args);
321 if (errno) _exit(111);
322 _exit(100);
323 }
324
325 wait_pid(&wstat,child);
326 if (wait_crashed(wstat)) return 1;
327
328 switch (wait_exitcode(wstat)) {
329 case 111: return 1;
330 default: return 0;
331 }
332}
333
335{
336 int c, i, j, k, l;
337 char *opt, *pos;
338
339 /* defaults: selector=default, IETF format, q=dns/txt, z=2, c=r */
340
341 if (!stralloc_copys(&sdid,"-d")) temp_nomem();
342 if (!stralloc_cat(&sdid,&sender)) temp_nomem();
343 if (!stralloc_0(&sdid)) temp_nomem();
344 if (!stralloc_copys(&selector,"-ydefault")) temp_nomem();
345 if (!stralloc_0(&selector)) temp_nomem();
346 if (!stralloc_copys(&selectore,"-Yeddy")) temp_nomem();
347 if (!stralloc_0(&selectore)) temp_nomem();
348 if (!stralloc_copys(&canon,"-cr")) temp_nomem();
349 if (!stralloc_0(&canon)) temp_nomem();
350 if (!stralloc_copys(&hash,"-z2")) temp_nomem();
351 if (!stralloc_0(&hash)) temp_nomem();
352
353 /* domain:selector,selectore|sdid|[auid|~]|expire|c:z:l; c=[r|s|t|u], z=[1,2,3,4,5], l=l */
354
355 if (dkimparams && *dkimparams) {
356 i = str_chr(dkimparams,'|');
357 pos = dkimparams + i;
358 if (*pos == '|' || *pos == '\0') { // selector
359 dkimparams[i] = '\0';
360 c = str_chr(dkimparams,','); // selectore=eddy
361 if (dkimparams[c] == ',') {
362 dkimparams[c] = '\0';
363 if (str_len(dkimparams + c + 1)) {
364 if (!stralloc_copys(&selectore,"-Y")) temp_nomem();
365 if (!stralloc_cats(&selectore,dkimparams + c + 1)) temp_nomem();
366 if (!stralloc_0(&selectore)) temp_nomem();
367 }
368 } else if (str_len(dkimparams)) { // selector=default
369 if (!stralloc_copys(&selector,"-y")) temp_nomem();
370 if (!stralloc_cats(&selector,dkimparams)) temp_nomem();
371 if (!stralloc_0(&selector)) temp_nomem();
372 }
373
374 j = str_chr(dkimparams + i + 1,'|');
375 pos = dkimparams + i + j + 1;
376 if (*pos == '|' || *pos == '\0') { // sdid; domain in DKIM header
377 dkimparams[i + j + 1] = '\0';
378 if (!stralloc_copys(&sdid,"-d")) temp_nomem();
379 if (!stralloc_cats(&sdid,dkimparams + i + 1)) temp_nomem();
380 if (!stralloc_0(&sdid)) temp_nomem();
381
382 k = str_chr(dkimparams + i + j + 2,'|');
383 pos = dkimparams + i + j + k + 2;
384 if (*pos == '|' || *pos == '\0') { // auid = identifier
385 dkimparams[i + j + k + 2] = '\0';
386 if (!stralloc_copys(&auid,"-i")) temp_nomem();
387 if (dkimparams[i + j + 2] == '~') {
388 if (!stralloc_cat(&auid,&originator)) temp_nomem();
389 } else
390 if (!stralloc_cats(&auid,dkimparams + i + j + 2)) temp_nomem();
391
392 if (!stralloc_0(&auid)) temp_nomem();
393
394 l = str_chr(dkimparams + i + j + k + 3,'|');
395 pos = dkimparams + i + j + k + l + 3;
396 if (*pos == '|' || *pos == '\0') { // expire after n secs
397 dkimparams[i + j + k + l + 3] = '\0';
398 if (!stralloc_copys(&expire,"-x")) temp_nomem();
399 if (!stralloc_cats(&expire,dkimparams + i + j + k + 3)) temp_nomem();
400 if (!stralloc_0(&expire)) temp_nomem();
401
402 /* Options to follow */
403
404 opt = dkimparams + i + j + k + l + 4;
405 if (*opt == '\0') return;
406 if (*opt != ':') {
407 if (!stralloc_copys(&canon,"-c")) temp_nomem(); // canonicalization
408 if (!stralloc_catb(&canon,opt,1)) temp_nomem();
409 if (!stralloc_0(&canon)) temp_nomem();
410 ++opt; if (*opt == '\0') return; // next colon
411 }
412 if (*opt != ':' || *opt == '\0') return;
413 if (*opt == ':') ++opt;
414 if (*opt != ':') {
415 if (!stralloc_copys(&hash,"-z")) temp_nomem(); // hash
416 if (!stralloc_catb(&hash,opt,1)) temp_nomem();
417 if (!stralloc_0(&hash)) temp_nomem();
418 ++opt; if (*opt == '\0') return; // next colon
419 }
420 if (*opt != ':' || *opt == '\0') return;
421 if (*opt == ':') ++opt;
422 if (*opt != ':' && *opt == 'l') {
423 if (!stralloc_copys(&length,"-l")) temp_nomem(); // length
424 if (!stralloc_0(&length)) temp_nomem();
425 }
426 }
427 }
428 }
429 }
430 }
431
432 return;
433}
434
435int main(int argc,char **args)
436{
437 int i;
438 int fdin = 0; // initial read from FD 0
439 int nkey = 0;
440 char *(qargs[4]);
441 struct stat st;
442
443 qargs[0] = args[0];
444 qargs[1] = args[1]; // host
445 qargs[2] = args[2]; // originator
446 qargs[3] = args[3]; // recipient
447
448 umask(033);
449 sig_pipeignore();
450 if (argc < 4) perm_usage();
451 if (chdir(auto_qmail) == -1) temp_chdir();
452
453 if (str_len(args[2]) > 2) {
454 i = str_chr(args[2],'@');
455 if (*(args[2] + i) == '@')
456 if (!stralloc_copys(&senddomain,args[2] + i + 1)) temp_nomem();
457 }
458 if (!stralloc_0(&senddomain)) temp_nomem();
459 if (!stralloc_copys(&originator,args[2])) temp_nomem();
460
461 if (!get_controls()) {
462 qmail_remote(qargs,fdin);
463 _exit(0);
464 }
465
466 dkim_setup(); // sender is evaluated from originator (senddomain)
467
468 /* Setup keys: they are composed from selector */
469
470 case_lowerb(sender.s,sender.len); // needs to be lowercase
472 if (!stralloc_cats(&rsakey,sender.s)) temp_nomem();
473 if (!stralloc_cats(&rsakey,"/")) temp_nomem();
474
476 if (!stralloc_cats(&ecckey,sender.s)) temp_nomem();
477 if (!stralloc_cats(&ecckey,"/")) temp_nomem();
478
479 /* RSA key common for SHA1 and SHA256: rsakeyfile -> selector */
480
481 if (!stralloc_cats(&rsakey,selector.s + 2)) temp_nomem(); // -y prepended
482 if (!stralloc_0(&rsakey)) temp_nomem();
483 if (stat(rsakey.s,&st) != -1)
484 if (open_read(rsakey.s) > 0) ++nkey;
485
486 /* ECC key follows: ecckeyfile -> (,)selector2 */
487
488 if (!stralloc_cats(&ecckey,selectore.s + 2)) temp_nomem(); // -Y prepended
489 if (!stralloc_0(&ecckey)) temp_nomem();
490 if (stat(ecckey.s,&st) != -1)
491 if (open_read(ecckey.s) > 0) ++nkey;
492
493 /* We got keys - go for staging */
494
495 if (nkey) { // otherwise no key exists; why bother
496 dkim_stage();
497 if (!dkim_sign(rsakey.s,ecckey.s,fndkin.s,fndkout.s)) {
498 fdin = open_read(fndkout.s);
499 if (fdin == -1) die_read();
500 } else {
501 fdin = open_read(fndkin.s); // DKIM key failed to sign
502 if (fdin == -1) die_read();
503 }
504 } else
506
507 qmail_remote(qargs,fdin); // closes fdin
508 if (nkey) dkim_unlink();
509
510 _exit(0);
511}
char auto_qmail[]
int main()
Definition: chkshsgr.c:6
void die_write()
Definition: columnt.c:18
int constmap_init(struct constmap *cm, char *s, int len, int flagcolon)
Definition: constmap.c:35
int control_readline(stralloc *sa, char *fn)
Definition: control.c:52
int control_readfile(stralloc *sa, char *fn, int flagme)
Definition: control.c:86
int control_init(void)
Definition: control.c:32
int stralloc_copys(stralloc *, char const *)
stralloc out
Definition: dnscname.c:12
void _exit()
unsigned int fmtqfn(char *s, char *dirslash, unsigned long id, int flagsplit)
Definition: fmtqfn.c:5
#define FMTQFN
Definition: fmtqfn.h:6
void c(char *, char *, char *, int, int, int)
Definition: install.c:57
int fd
char * dkimparams
Definition: qmail-dksign.c:79
void temp_unlink()
Definition: qmail-dksign.c:98
char bufin[1000]
Definition: qmail-dksign.c:56
void zero()
Definition: qmail-dksign.c:65
void temp_read()
Definition: qmail-dksign.c:113
stralloc rsakey
Definition: qmail-dksign.c:78
void temp_chdir()
Definition: qmail-dksign.c:86
void temp_create()
Definition: qmail-dksign.c:91
stralloc length
Definition: qmail-dksign.c:244
stralloc dkimdomains
Definition: qmail-dksign.c:74
stralloc originator
Definition: qmail-dksign.c:73
void perm_usage()
Definition: qmail-dksign.c:108
stralloc canon
Definition: qmail-dksign.c:242
#define DOMAINKEYS
Definition: qmail-dksign.c:31
stralloc selector
Definition: qmail-dksign.c:237
void dkim_setup()
Definition: qmail-dksign.c:334
void temp_control()
Definition: qmail-dksign.c:103
stralloc sender
Definition: qmail-dksign.c:71
int qmail_remote(char **qargs, int fd)
Definition: qmail-dksign.c:303
stralloc auid
Definition: qmail-dksign.c:240
stralloc ecckey
Definition: qmail-dksign.c:77
void dkim_unlink()
Definition: qmail-dksign.c:189
void temp_nosignkey()
Definition: qmail-dksign.c:118
stralloc fndkout
Definition: qmail-dksign.c:69
stralloc fndkin
Definition: qmail-dksign.c:68
void die_read()
Definition: qmail-dksign.c:63
void temp_nomem()
Definition: qmail-dksign.c:81
stralloc hash
Definition: qmail-dksign.c:243
int dkim_sign(const char *rsakeyfile, const char *ecckeyfile, const char *fnin, const char *fnout)
Definition: qmail-dksign.c:264
stralloc sdid
Definition: qmail-dksign.c:239
buffer bi
Definition: qmail-dksign.c:57
void fnmake_dkim(unsigned long id)
Definition: qmail-dksign.c:182
stralloc selectore
Definition: qmail-dksign.c:238
void dkim_stage()
Definition: qmail-dksign.c:197
buffer bo
Definition: qmail-dksign.c:59
struct constmap mapdkimdomains
Definition: qmail-dksign.c:75
stralloc senddomain
Definition: qmail-dksign.c:72
stralloc expire
Definition: qmail-dksign.c:241
void zerodie()
Definition: qmail-dksign.c:66
int get_controls()
Definition: qmail-dksign.c:128
char bufout[1000]
Definition: qmail-dksign.c:58
void die()
Definition: qmail-pop3d.c:24
stralloc fn
Definition: qmail-qmaint.c:483
unsigned long id
Definition: qmail-qread.c:52
int j
Definition: qmail-send.c:920
int fdin
Definition: qmail-todo.c:144
int rcpthosts_init()
Definition: rcpthosts.c:22
int rcpthosts()
uint32_t k[64]
Definition: sha256.c:26
void write()