s/qmail 4.3.25
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 signdomain = {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 int i;
148
149 fd = open_read(fndkin.s);
150 if (fd == -1) die_read();
151 buffer_init(&bi,buffer_unixread,fd,inbuf,sizeof(inbuf));
152
153 if (!stralloc_copys(&signdomain,"")) temp_nomem();
154
155 for (;;) {
156 if (getln(&bi,&line,&match,'\n') == -1) temp_read();
157 if (case_starts(line.s,"DKIM-Signature: ")) r = 1;
158 if (r) { // DKIM signature seen
159 for (i = 1; i < line.len; i++) { // d=domain.tld;
160 if (r == 1 && *(line.s + i) == '=' && *(line.s + i - 1) == 'd') r = 2;
161 if (r == 2) {
162 ch = *(line.s + i); // next character to fetch
163 if (ch == ';') { r = 3; goto DONE; } // done
164 if (ch == '\n') break; // next line
165 if (ch != '=' && ch != ' ' && ch != '\t' && ch != '\r')
166 if (!stralloc_catb(&signdomain,&ch,1)) temp_nomem();
167 }
168 }
169 }
170 if (!match) break;
171 }
172
173 DONE:
174 if (r != 3)
175 if (!stralloc_copys(&signdomain,"unknown")) temp_nomem();
176 if (!stralloc_0(&signdomain)) temp_nomem();
177
178 return r;
179}
180
181static int dkim_verify()
182{
183 int child;
184 int wstat;
185 char *(args[6]);
186 int r = -1;
187
188 args[0] = "qmail-dkim";
189 args[1] = "-V";
190 args[2] = fndkin.s;
191 args[3] = "none";
192 args[4] = fndkout.s;
193 args[5] = 0;
194
195 if (!(child = fork())) {
196 pathexec(args);
197 if (errno) _exit(111);
198 _exit(100);
199 }
200
201 wait_pid(&wstat,child);
202 if (wait_crashed(wstat)) return 1;
203
204 switch (r = wait_exitcode(wstat)) {
205 case 10: return 1;
206 default: return 0;
207 }
208}
209
210static int dkim_result(const char *me)
211{
212 int max = 80;
213 int fd;
214 char ch;
215 int r = 0;
216
217 if (!stralloc_copys(&result,"")) temp_nomem();
218
219 if ((fd = open_read(fndkout.s)) == -1) return 0; // nothing to read
220 while ((r = read(fd,inbuf,sizeof(inbuf))) > 0)
221 if (!stralloc_catb(&result,inbuf,r)) temp_nomem();
222
223 if (!stralloc_0(&result)) temp_nomem();
224
225 if (result.len > 2) {
226 if (case_starts(result.s,"pass")) r = 0;
227 if (case_starts(result.s,"fail")) r = 35;
228 } else
229 if (!stralloc_copys(&result,"unknown")) temp_nomem();
230
231 if (!stralloc_copys(&dkheader,"X-Authentication-Results: ")) temp_nomem();
232 if (!stralloc_cats(&dkheader,signdomain.s)) temp_nomem();
233 if (!stralloc_cats(&dkheader,"; dkim=")) temp_nomem();
234
235 // dkim=fail" (signature verify error: message body does not hash to bh=
236
237 for (int j = 0; j < result.len; j++) { // FIXME
238 ch = result.s[j];
239 if (ch == '\r' || ch == '\n' || ch == '\0') continue;
240 if (j <= max) if (!stralloc_catb(&dkheader,&ch,1)) temp_nomem();
241 if (ch == ' ' && (j > max)) {
242 if (!stralloc_cats(&dkheader,"\n ")) temp_nomem();
243 max += j;
244 }
245 }
246
247 if (!stralloc_cats(&dkheader,"; ")) temp_nomem();
248 if (!stralloc_cats(&dkheader,me)) temp_nomem();
249 if (!stralloc_0(&dkheader)) temp_nomem();
250
251 return r;
252}
253
254static int qmail_queue()
255{
256 char ch;
257 int fd;
258 int r;
259 int child;
260 int wstat;
261 int pi[2];
262 char *(args[2]);
263
264 if (pipe(pi) == -1) die_pipe(fndkin.s);
265
266 args[0] = "qmail-queue";
267 args[1] = 0;
268
269 switch (child = vfork()) {
270 case -1:
271 close(pi[0]); close(pi[1]);
272 die_write(fndkin.s);
273 case 0:
274 close(pi[1]);
275 if (fd_move(0,pi[0]) == -1) die_pipe(fndkin.s);
276 sig_pipedefault();
277 pathexec(args);
278 if (errno) _exit(111);
279 _exit(100);
280 }
281 close(pi[0]);
282
283 buffer_init(&bo,buffer_unixwrite,pi[1],outbuf,sizeof(outbuf));
284
285 if (dkheader.len > 2) { // write DKIM header
286 if (buffer_put(&bo,dkheader.s,dkheader.len - 1) == -1) die_write(fndkout.s);
287 if (buffer_put(&bo,"\n",1) == -1) die_write(fndkout.s);
288 if (buffer_flush(&bo) == -1) die_write(fndkout.s);
289 }
290
291 /* read/write message; we need to remove the CR (ok) */
292
293 if ((fd = open_read(fndkin.s)) == -1) die_read();
294 while ((r = read(fd,&ch,1)) > 0)
295 if (ch != '\r')
296 if (buffer_put(&bo,&ch,1) == -1) die_write(fndkin.s);
297
298 if (buffer_flush(&bo) == -1) die_write(fndkin.s);
299 close(pi[1]);
300
301 wait_pid(&wstat,child);
302 if (wait_crashed(wstat)) return 1;
303
304 switch (r = wait_exitcode(wstat)) {
305 case 10: return 1;
306 default: return 0;
307 }
308}
309
310static void dkim_unlink()
311{
312 if (unlink(fndkin.s) == -1)
313 if (errno != ENOENT) temp_unlink();
314 if (unlink(fndkout.s) == -1)
315 if (errno != ENOENT) temp_unlink();
316}
317
318int main()
319{
320 int r = 0;
321 char *mode = 0;
322
323 umask(033);
324 if (chdir(auto_qmail) == -1) temp_chdir();
325 if (control_init() == -1) temp_control();
326 if (control_readline(&me,"control/me") == -1) temp_control();
327 if (!stralloc_0(&me)) temp_nomem();
328
329 dkim_stage();
330
331 if (mess_dkim()) {
332 dkim_verify();
333 r = dkim_result(me.s);
334 }
335
336 /* we are done: eventually call qmail-queue */
337
338 mode = env_get("DKIM");
339 if (!mode || *mode != '+') r = 0;
340 if (!r) qmail_queue();
341 dkim_unlink();
342
343 _exit(r);
344}
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
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