diff options
author | Jannis Hoffmann <jannis@fehcom.de> | 2024-07-09 11:44:11 +0200 |
---|---|---|
committer | Jannis Hoffmann <jannis@fehcom.de> | 2024-07-09 11:44:11 +0200 |
commit | f1b71c9fe7dbb4886588a036399cf5ebe16b7c47 (patch) | |
tree | e07786aa479c9fb6ee3e537078470aaab5454f80 /src/qmail-dkverify.c | |
parent | a293489ee83c8b05d845a162dc2a4de026f3775d (diff) |
removed top level directory
Diffstat (limited to 'src/qmail-dkverify.c')
-rw-r--r-- | src/qmail-dkverify.c | 368 |
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); +} |