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