s/qmail 4.3.20
Next generation secure email transport
Loading...
Searching...
No Matches
qmail-dkverify.c
Go to the documentation of this file.
1#include <sys/types.h>
2#include <sys/stat.h>
3#include <unistd.h>
4#include <fcntl.h>
5#include <sys/socket.h>
6#include "sig.h"
7#include "stralloc.h"
8#include "buffer.h"
9#include "error.h"
10#include "auto_qmail.h"
11#include "auto_queue.h"
12#include "str.h"
13#include "exit.h"
14#include "uint_t.h"
15#include "fd.h"
16#include "open.h"
17#include "fmt.h"
18#include "fmtqfn.h"
19#include "readwrite.h"
20#include "getln.h"
21#include "qmail.h"
22#include "wait.h"
23#include "byte.h"
24#include "case.h"
25#include "control.h"
26#include "pathexec.h"
27#include "env.h"
28
29#define WHO "qmail-dkverify"
30
45buffer bi = BUFFER_INIT(buffer_unixread,0,inbuf,sizeof(inbuf)); // read buffer
47buffer bo = BUFFER_INIT(buffer_unixwrite,1,outbuf,sizeof(outbuf)); // output message
48
49static void die(int e) { _exit(e); }
50static void die_pipe(char *fn) { unlink(fn); die(54); };
51static void die_write(char *fn) { unlink(fn); die(62); };
52static void die_read() { die(54); };
53static void out(char *s) { if (buffer_puts(&bo,s) == -1) _exit(111); }
54static void zero() { if (buffer_put(&bo,"\0",1) == -1) _exit(111); }
55static void zerodie() { zero(); buffer_flush(&bo); _exit(111); }
56
57static void temp_nomem()
58{
59 out("ZOut of memory. (#4.3.0)\n");
60 zerodie();
61}
62static void temp_chdir()
63{
64 out("ZUnable to switch to target directory. (#4.3.0)\n");
65 zerodie();
66}
67static void temp_unlink()
68{
69 out("ZUnable to unlink DKIM stage file. (#4.3.0)\n");
70 zerodie();
71}
72static void temp_read()
73{
74 out("ZUnable to read message. (#4.3.0)\n");
75 zerodie();
76}
77static void temp_control()
78{
79 out("ZUnable to read control files. (#4.3.0)\n");
80 zerodie();
81}
82
83static stralloc me = {0};
84static stralloc senddomain = {0};
85static stralloc dkheader = {0};
86static stralloc fndkin = {0};
87static stralloc fndkout = {0};
88static stralloc result = {0};
89
90static stralloc temp = {0};
91
92static void fnmake_dkim(unsigned long id)
93{
94 fndkin.len = fmtqfn(fndkin.s,"queue/dkim/",id,1);
95 id += id;
96 fndkout.len = fmtqfn(fndkout.s,"queue/dkim/",id,1);
97}
98
99static void dkim_stage()
100{
101 int r;
102 int fd;
103 int in, out;
104 struct stat st;
105 char tmpbuf[BUFSIZE_MESS + 2];
106
107 if (chdir(auto_queue) == -1) temp_chdir();
108
109 if (!stralloc_ready(&fndkin,FMTQFN)) temp_nomem();
110 if (!stralloc_ready(&fndkout,FMTQFN)) temp_nomem();
111
112 fnmake_dkim(getpid()); // pre-staging
113 fd = open_excl(fndkin.s);
114 if (fd == -1) die_write(fndkin.s);
115
116 buffer_init(&bi,buffer_unixread,0,inbuf,sizeof(inbuf));
117 buffer_init(&bo,buffer_unixwrite,fd,outbuf,sizeof(outbuf));
118
119 while ((r = buffer_get(&bi,inbuf,sizeof(inbuf))) > 0) { // read into buffer
120 for (in = out = 0; in < r; in++) { // reconstruct CRLF (ok)
121 if (!(inbuf[in] == '\n' || inbuf[in] == '\r')) {
122 tmpbuf[out] = inbuf[in];
123 out++;
124 } else {
125 tmpbuf[out++] = '\r';
126 tmpbuf[out++] = '\n';
127 if (!stralloc_copyb(&temp,tmpbuf,out)) temp_nomem();
128 if (!stralloc_0(&temp)) temp_nomem();
129 }
130 }
131 if (out) buffer_put(&bo,tmpbuf,out);
132 }
133
134 if (buffer_flush(&bo) == -1) die(51);
135 if (fstat(fd,&st) == -1) die_write(fndkin.s);
136 if (fsync(fd) == -1) die_write(fndkin.s);
137 if (close(fd) == -1) die_write(fndkin.s);
138}
139
140static int mess_dkim()
141{
142 stralloc line = {0};
143 char ch;
144 int match;
145 int fd;
146 int r = 0;
147
148 fd = open_read(fndkin.s);
149 if (fd == -1) die_read();
150 buffer_init(&bi,buffer_unixread,fd,inbuf,sizeof(inbuf));
151
153
154 for (;;) {
155 if (getln(&bi,&line,&match,'\n') == -1) temp_read();
156 if (case_starts(line.s,"DKIM-Signature: ")) r = 1;
157 if (r == 1) { // DKIM signature seen
158 for (int i = 0; i < line.len; i++) { // d=domain.tld
159 if (*(line.s + i) == '=' && *(line.s + i - 1) == 'd') r = 2;
160 if (r) {
161 ch = *(line.s + i); // next character to fetch
162 if (ch == '\n') break; // next line
163 if (ch == ';') { r = 3; break; } // done
164 if (ch != '=' && ch != ' ' && ch != '\t' && ch != '\r')
165 if (!stralloc_catb(&senddomain,&ch,1)) temp_nomem();
166 }
167 }
168 }
169 if (r == 3 || !match) break;
170 }
171 if (senddomain.len < 2)
172 if (!stralloc_copys(&senddomain,"unknown")) temp_nomem();
173 if (!stralloc_0(&senddomain)) temp_nomem();
174
175 return r;
176}
177
178static int dkim_verify()
179{
180 int child;
181 int wstat;
182 char *(args[6]);
183 int r = -1;
184
185 args[0] = "qmail-dkim";
186 args[1] = "-V";
187 args[2] = fndkin.s;
188 args[3] = "none";
189 args[4] = fndkout.s;
190 args[5] = 0;
191
192 if (!(child = fork())) {
193 pathexec(args);
194 if (errno) _exit(111);
195 _exit(100);
196 }
197
198 wait_pid(&wstat,child);
199 if (wait_crashed(wstat)) return 1;
200
201 switch (r = wait_exitcode(wstat)) {
202 case 10: return 1;
203 default: return 0;
204 }
205}
206
207static int dkim_result(const char *me)
208{
209 int max = 80;
210 int fd;
211 char ch;
212 int r = 0;
213
214 if (!stralloc_copys(&result,"")) temp_nomem();
215
216 if ((fd = open_read(fndkout.s)) == -1) return 0; // nothing to read
217 while ((r = read(fd,inbuf,sizeof(inbuf))) > 0)
218 if (!stralloc_catb(&result,inbuf,r)) temp_nomem();
219
220 if (!stralloc_0(&result)) temp_nomem();
221
222 if (result.len > 2) {
223 if (case_starts(result.s,"pass")) r = 0;
224 if (case_starts(result.s,"fail")) r = 35;
225 } else
226 if (!stralloc_copys(&result,"unknown")) temp_nomem();
227
228 if (!stralloc_copys(&dkheader,"X-Authentication-Results: ")) temp_nomem();
229 if (!stralloc_cats(&dkheader,senddomain.s)) temp_nomem();
230 if (!stralloc_cats(&dkheader,"; dkim=")) temp_nomem();
231
232 // dkim=fail" (signature verify error: message body does not hash to bh=
233
234 for (int j = 0; j < result.len; j++) { // FIXME
235 ch = result.s[j];
236 if (ch == '\r' || ch == '\n' || ch == '\0') continue;
237 if (j <= max) if (!stralloc_catb(&dkheader,&ch,1)) temp_nomem();
238 if (ch == ' ' && (j > max)) {
239 if (!stralloc_cats(&dkheader,"\n ")) temp_nomem();
240 max += j;
241 }
242 }
243
244 if (!stralloc_cats(&dkheader,"; ")) temp_nomem();
245 if (!stralloc_cats(&dkheader,me)) temp_nomem();
246 if (!stralloc_0(&dkheader)) temp_nomem();
247
248 return r;
249}
250
251static int qmail_queue()
252{
253 char ch;
254 int fd;
255 int r;
256 int child;
257 int wstat;
258 int pi[2];
259 char *(args[2]);
260
261 if (pipe(pi) == -1) die_pipe(fndkin.s);
262
263 args[0] = "qmail-queue";
264 args[1] = 0;
265
266 switch (child = vfork()) {
267 case -1:
268 close(pi[0]); close(pi[1]);
269 die_write(fndkin.s);
270 case 0:
271 close(pi[1]);
272 if (fd_move(0,pi[0]) == -1) die_pipe(fndkin.s);
273 sig_pipedefault();
274 pathexec(args);
275 if (errno) _exit(111);
276 _exit(100);
277 }
278 close(pi[0]);
279
280 buffer_init(&bo,buffer_unixwrite,pi[1],outbuf,sizeof(outbuf));
281
282 if (dkheader.len > 2) { // write DKIM header
283 if (buffer_put(&bo,dkheader.s,dkheader.len - 1) == -1) die_write(fndkout.s);
284 if (buffer_put(&bo,"\n",1) == -1) die_write(fndkout.s);
285 if (buffer_flush(&bo) == -1) die_write(fndkout.s);
286 }
287
288 /* read/write message; we need to remove the CR (ok) */
289
290 if ((fd = open_read(fndkin.s)) == -1) die_read();
291 while ((r = read(fd,&ch,1)) > 0)
292 if (ch != '\r')
293 if (buffer_put(&bo,&ch,1) == -1) die_write(fndkin.s);
294
295 if (buffer_flush(&bo) == -1) die_write(fndkin.s);
296 close(pi[1]);
297
298 wait_pid(&wstat,child);
299 if (wait_crashed(wstat)) return 1;
300
301 switch (r = wait_exitcode(wstat)) {
302 case 10: return 1;
303 default: return 0;
304 }
305}
306
307static void dkim_unlink()
308{
309 if (unlink(fndkin.s) == -1)
310 if (errno != ENOENT) temp_unlink();
311 if (unlink(fndkout.s) == -1)
312 if (errno != ENOENT) temp_unlink();
313}
314
315int main()
316{
317 int r = 0;
318 char *mode = 0;
319
320 umask(033);
321 if (chdir(auto_qmail) == -1) temp_chdir();
322 if (control_init() == -1) temp_control();
323 if (control_readline(&me,"control/me") == -1) temp_control();
324 if (!stralloc_0(&me)) temp_nomem();
325
326 dkim_stage();
327
328 if (mess_dkim()) {
329 dkim_verify();
330 r = dkim_result(me.s);
331 }
332
333 /* we are done: call qmail-queue */
334
335 mode = env_get("DKIM");
336 if (!mode || *mode != '+') r = 0;
337
338 qmail_queue();
339 dkim_unlink();
340
341 _exit(r);
342}
char auto_qmail[]
char auto_queue[]
void die_write()
Definition: columnt.c:17
void die_read()
Definition: columnt.c:16
int control_readline(stralloc *sa, char *fn)
Definition: control.c:53
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
stralloc line
Definition: maildir2mbox.c:27
int match
Definition: matchup.c:196
stralloc me
Definition: newaliases.c:46
char tmpbuf[BUFSIZE_LINE]
Definition: newinclude.c:38
int fd
stralloc fndkout
Definition: qmail-dksign.c:70
stralloc fndkin
Definition: qmail-dksign.c:69
stralloc senddomain
Definition: qmail-dksign.c:73
char outbuf[BUFSIZE_MESS]
buffer bi
buffer bo
int main()
char inbuf[BUFSIZE_LINE]
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
#define BUFSIZE_MESS
Definition: qmail.h:7
#define BUFSIZE_LINE
Definition: qmail.h:8
void zerodie(void)
Definition: qmail-remote.c:123
void temp_nomem(void)
Definition: qmail-ldapam.c:65