summaryrefslogtreecommitdiff
path: root/src/qmail-dkverify.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/qmail-dkverify.c')
-rw-r--r--src/qmail-dkverify.c368
1 files changed, 368 insertions, 0 deletions
diff --git a/src/qmail-dkverify.c b/src/qmail-dkverify.c
new file mode 100644
index 0000000..2cfe00a
--- /dev/null
+++ b/src/qmail-dkverify.c
@@ -0,0 +1,368 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include "sig.h"
+#include "stralloc.h"
+#include "buffer.h"
+#include "error.h"
+#include "auto_qmail.h"
+#include "str.h"
+#include "exit.h"
+#include "uint_t.h"
+#include "fd.h"
+#include "open.h"
+#include "fmt.h"
+#include "fmtqfn.h"
+#include "readwrite.h"
+#include "getln.h"
+#include "qmail.h"
+#include "wait.h"
+#include "byte.h"
+#include "case.h"
+#include "control.h"
+#include "pathexec.h"
+#include "env.h"
+#include "logmsg.h"
+
+#define WHO "qmail-dkverify"
+
+/** @file qmail-dkverify.c
+ @brief stub routine for DKIM signature verification and indication in received message
+
+ Steps:
+ ------
+ a) Store message with CRLF
+ b) Get DKIM signature from message - if given:
+ c) Call qmail-dkim for verification
+ d) Include results as appended header
+ e) Queue the message for processing
+
+ */
+
+char inbuf[BUFSIZE_LINE];
+buffer bi = BUFFER_INIT(read,0,inbuf,sizeof(inbuf)); // read buffer
+char outbuf[BUFSIZE_MESS];
+buffer bo = BUFFER_INIT(write,1,outbuf,sizeof(outbuf)); // output message
+
+void die(int e) { _exit(e); }
+void die_pipe(char *fn) { unlink(fn); die(53); };
+void die_write(char *fn) { unlink(fn); die(53); };
+void die_read() { die(54); };
+void out(char *s) { if (buffer_puts(&bo,s) == -1) _exit(111); }
+void zero() { if (buffer_put(&bo,"\0",1) == -1) _exit(111); }
+void zerodie() { zero(); buffer_flush(&bo); _exit(111); }
+
+void temp_nomem()
+{
+ out("ZOut of memory. (#4.3.0)\n");
+ zerodie();
+}
+void temp_chdir()
+{
+ out("ZUnable to switch to target directory. (#4.3.0)\n");
+ zerodie();
+}
+void temp_create()
+{
+ out("ZUnable to create DKIM stage file. (#4.3.0)\n");
+ zerodie();
+}
+void temp_unlink()
+{
+ out("ZUnable to unlink DKIM stage file. (#4.3.0)\n");
+ zerodie();
+}
+void temp_read()
+{
+ out("ZUnable to read message. (#4.3.0)\n");
+ zerodie();
+}
+void temp_socket()
+{
+ out("ZUnable to crate socket pair. (#4.3.0)\n");
+ zerodie();
+}
+void temp_control()
+{
+ out("ZUnable to read control files. (#4.3.0)\n");
+ zerodie();
+}
+
+static stralloc me = {0};
+static stralloc senddomain = {0};
+static stralloc dkheader = {0};
+static stralloc fndkin = {0};
+static stralloc fndkout = {0};
+static stralloc result = {0};
+
+void fnmake_dkim(unsigned long id)
+{
+ fndkin.len = fmtqfn(fndkin.s,"queue/dkim/",id,1);
+ id += id;
+ fndkout.len = fmtqfn(fndkout.s,"queue/dkim/",id,1);
+}
+
+void dkim_stage()
+{
+ int r;
+ int fd;
+ int in, out;
+ struct stat st;
+ char tmpbuf[BUFSIZE_MESS + 2];
+
+ if (!stralloc_ready(&fndkin,FMTQFN)) temp_nomem();
+ if (!stralloc_ready(&fndkout,FMTQFN)) temp_nomem();
+
+ fnmake_dkim(getpid()); // pre-staging
+ fd = open_excl(fndkin.s);
+ if (fd == -1) die_write(fndkin.s);
+
+ buffer_init(&bi,read,0,inbuf,sizeof(inbuf));
+ buffer_init(&bo,write,fd,outbuf,sizeof(outbuf));
+
+ while((r = buffer_get(&bi,inbuf,sizeof(inbuf)))) { // read into buffer
+ if (r == -1) temp_read();
+
+ for (in = out = 0; in < r; in++) { // reconstruct CRLF (ok)
+ if (inbuf[in] != '\n') {
+ tmpbuf[out++] = inbuf[in];
+ } else {
+ tmpbuf[out++] = '\r';
+ tmpbuf[out++] = '\n';
+ }
+ }
+ if (out) buffer_put(&bo,tmpbuf,out);
+ }
+
+ if (buffer_flush(&bo) == -1) die(51);
+ if (fstat(fd,&st) == -1) die_write(fndkin.s);
+ if (fsync(fd) == -1) die_write(fndkin.s);
+ if (close(fd) == -1) die_write(fndkin.s);
+}
+
+int mess_dkim()
+{
+ stralloc line = {0};
+ int match;
+ int fd;
+ int at = 0;
+ int ket = 0;
+ int end = 0;
+ int len = 0;
+ int r = 0;
+ int i;
+
+ fd = open_read(fndkin.s);
+ if (fd == -1) die_read();
+ buffer_init(&bi,read,fd,inbuf,sizeof(inbuf));
+
+ if (!stralloc_copys(&senddomain,"")) temp_nomem();
+
+ for (;;) {
+ if (getln(&bi,&line,&match,'\n') == -1) temp_read();
+ if (case_starts(line.s,"DKIM-Signature: ")) r = 1;
+ if (r == 1) {
+ if (case_starts(line.s,"From: ")) { // fallback: From
+ at = str_chr(line.s,'@');
+ if (at < line.len) {
+ end = str_chr(line.s,'\n'); // From: user@senddomain\n
+ ket = str_chr(line.s,'>'); // From: User <user@senddomain>
+ len = (ket < end) ? ket : end;
+ if (!stralloc_copyb(&senddomain,line.s + at + 1,len - at - 1)) temp_nomem();
+ r = 2;
+ }
+ }
+ for (i = 0; i < line.len; ++i) { // d=domain.tld
+ if (*(line.s + i) == '=' && *(line.s + i - 1) == 'd') {
+ ++i; // gotcha
+ while (*(line.s + i) != ';') {
+ if (!stralloc_catb(&senddomain,line.s + i,1)) temp_nomem();
+ i++;
+ r = 3;
+ }
+ }
+ }
+ }
+ if (r >= 2 || !match) break;
+ }
+ if (senddomain.len < 2)
+ if (!stralloc_copys(&senddomain,"unknown")) temp_nomem();
+ if (!stralloc_0(&senddomain)) temp_nomem();
+
+ return r;
+}
+
+int dkim_verify()
+{
+ int child;
+ int wstat;
+ char *(args[6]);
+ int r = -1;
+
+ args[0] = "qmail-dkim";
+ args[1] = "-V";
+ args[2] = fndkin.s;
+ args[3] = "none";
+ args[4] = fndkout.s;
+ args[5] = 0;
+
+ if (!(child = fork())) {
+ pathexec(args);
+ if (errno) _exit(111);
+ _exit(100);
+ }
+
+ wait_pid(&wstat,child);
+ if (wait_crashed(wstat)) return 1;
+
+ switch (r = wait_exitcode(wstat)) {
+ case 10: return 1;
+ default: return 0;
+ }
+}
+
+int dkim_result(const char *me)
+{
+ int max = 64;
+ int fd;
+ int j;
+ char ch;
+ int r = 0;
+
+ if (!stralloc_copys(&result,"")) temp_nomem();
+
+ if ((fd = open_read(fndkout.s)) == -1) return 0; // nothing to read
+ while ((r = read(fd,inbuf,sizeof(inbuf))) > 0)
+ if (!stralloc_catb(&result,inbuf,r)) temp_nomem();
+
+ if (!stralloc_0(&result)) temp_nomem();
+
+ if (result.len > 2) {
+ if (case_starts(result.s,"pass")) r = 0;
+ if (case_starts(result.s,"fail")) r = 35;
+ } else
+ if (!stralloc_copys(&result,"unknown")) temp_nomem();
+
+ if (!stralloc_copys(&dkheader,"X-Authentication-Results: ")) temp_nomem();
+ if (!stralloc_cats(&dkheader,senddomain.s)) temp_nomem();
+ if (!stralloc_cats(&dkheader,"; dkim=")) temp_nomem();
+
+ for (j = 0; j < result.len; j++) {
+ ch = result.s[j];
+ if (ch == '\r' || ch == '\n' || ch == '\0') continue;
+ if (j <= max) if (!stralloc_catb(&dkheader,&ch,1)) temp_nomem();
+ if (ch == ' ' && (j > max)) {
+ if (!stralloc_cats(&dkheader,"\n ")) temp_nomem();
+ max += max;
+ }
+ }
+
+ if (!stralloc_cats(&dkheader,"; ")) temp_nomem();
+ if (!stralloc_cats(&dkheader,me)) temp_nomem();
+ if (!stralloc_0(&dkheader)) temp_nomem();
+
+ return r;
+}
+
+int qmail_queue()
+{
+ int fd;
+ int r;
+ int child;
+ int wstat;
+ int pi[2];
+ char *(args[2]);
+ char tmpbuf[BUFSIZE_MESS];
+ int in, out;
+
+ if (pipe(pi) == -1) die_pipe(fndkin.s);
+
+ args[0] = "qmail-queue";
+ args[1] = 0;
+
+ switch (child = vfork()) {
+ case -1:
+ close(pi[0]); close(pi[1]);
+ die_write(fndkin.s);
+ case 0:
+ close(pi[1]);
+ if (fd_move(0,pi[0]) == -1) die_pipe(fndkin.s);
+ sig_pipedefault();
+ pathexec(args);
+ if (errno) _exit(111);
+ _exit(100);
+ }
+ close(pi[0]);
+
+ buffer_init(&bo,write,pi[1],outbuf,sizeof(outbuf));
+
+ if (dkheader.len > 2) { // write DKIM header
+ if (buffer_put(&bo,dkheader.s,dkheader.len - 1) == -1) die_write(fndkout.s);
+ if (buffer_put(&bo,"\n",1) == -1) die_write(fndkout.s);
+ if (buffer_flush(&bo) == -1) die_write(fndkout.s);
+ }
+
+ /* read/write message; we need to remove the CR (ok) */
+
+ if ((fd = open_read(fndkin.s)) == -1) die_read();
+ while ((r = read(fd,tmpbuf,sizeof(tmpbuf))) > 0) {
+ for (in = 0, out = 0; in < r; ++in) {
+ if (tmpbuf[in] == '\r') {
+ buffer_put(&bo,&tmpbuf[out],in - out);
+ out = in + 1; // \n to follow
+ }
+ }
+ }
+
+ if (buffer_flush(&bo) == -1) die_write(fndkin.s);
+ close(pi[1]);
+
+ wait_pid(&wstat,child);
+ if (wait_crashed(wstat)) return 1;
+
+ switch (r = wait_exitcode(wstat)) {
+ case 10: return 1;
+ default: return 0;
+ }
+
+ return 0;
+}
+
+void dkim_unlink()
+{
+ if (unlink(fndkin.s) == -1)
+ if (errno != ENOENT) temp_unlink();
+ if (unlink(fndkout.s) == -1)
+ if (errno != ENOENT) temp_unlink();
+}
+
+int main()
+{
+ int r = 0;
+ char *mode = 0;
+
+ umask(033);
+ if (chdir(auto_qmail) == -1) temp_chdir();
+ if (control_init() == -1) temp_control();
+ if (control_readline(&me,"control/me") == -1) temp_control();
+ if (!stralloc_0(&me)) temp_nomem();
+
+ dkim_stage();
+
+ if (mess_dkim()) {
+ dkim_verify();
+ r = dkim_result(me.s);
+ }
+
+ /* we are done: call qmail-queue */
+
+ mode = env_get("DKIM");
+ if (!mode || *mode != '+') r = 0;
+
+ qmail_queue();
+ dkim_unlink();
+
+ _exit(r);
+}