crm114-exec.c 5.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
/*
 * crm114 backend for dovecot antispam plugin
 *
 * Copyright (C) 2004-2007  Johannes Berg <johannes@sipsolutions.net>
 *                    2006  Frank Cusack
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License Version 2 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 */

#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <fcntl.h>

#include "lib.h"
#include "mail-storage-private.h"

#include "antispam-plugin.h"


Johannes Berg's avatar
Johannes Berg committed
32 33
static int call_reaver(const struct antispam_config *cfg,
		       const char *signature, enum classification wanted)
34 35 36 37 38 39 40
{
	pid_t pid;
	const char *class_arg;
	int pipes[2];

	switch (wanted) {
	case CLASS_NOTSPAM:
Johannes Berg's avatar
Johannes Berg committed
41
		class_arg = "--good";
42 43
		break;
	case CLASS_SPAM:
Johannes Berg's avatar
Johannes Berg committed
44
		class_arg = "--spam";
45 46 47 48 49 50 51
		break;
	}

	/*
	 * For reaver stdin, it wants to read a full message but
	 * really only needs the signature.
	 */
Johannes Berg's avatar
Johannes Berg committed
52 53
	if (pipe(pipes))
		return -1;
54 55 56 57 58 59 60 61 62 63 64 65 66

	pid = fork();
	if (pid < 0)
		return -1;

	if (pid) {
		int status;

		close(pipes[0]);

		/*
		 * Reaver wants the mail but only needs the cache ID
		 */
Johannes Berg's avatar
Johannes Berg committed
67 68
		write(pipes[1], cfg->crm.sigcfg.signature_hdr,
			strlen(cfg->crm.sigcfg.signature_hdr));
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
		write(pipes[1], ": ", 2);
		write(pipes[1], signature, strlen(signature));
		write(pipes[1], "\r\n\r\n", 4);
		close(pipes[1]);

		/*
		 * Wait for reaver
		 */
		waitpid(pid, &status, 0);
		if (!WIFEXITED(status))
			return 1;

		return WEXITSTATUS(status);
	} else {
		int fd = open("/dev/null", O_RDONLY);
		char **argv;
85
		/* 2 fixed, extra, terminating NULL */
Johannes Berg's avatar
Johannes Berg committed
86
		int sz = sizeof(char *) * (2 + cfg->crm.extra_args_num + 1);
87 88 89 90 91 92 93 94 95 96 97
		int i;

		argv = i_malloc(sz);
		memset(argv, 0, sz);

		close(0);
		close(1);
		close(2);
		/* see above */
		close(pipes[1]);

Johannes Berg's avatar
Johannes Berg committed
98
		if (dup2(pipes[0], 0) != 0)
99 100 101
			exit(1);
		close(pipes[0]);

Johannes Berg's avatar
Johannes Berg committed
102
		if (dup2(fd, 1) != 1)
103
			exit(1);
Johannes Berg's avatar
Johannes Berg committed
104
		if (dup2(fd, 2) != 2)
105 106 107
			exit(1);
		close(fd);

Johannes Berg's avatar
Johannes Berg committed
108
		argv[0] = (char *)cfg->crm.reaver_binary;
109 110
		argv[1] = (char *)class_arg;

Johannes Berg's avatar
Johannes Berg committed
111 112
		for (i = 0; i < cfg->crm.extra_args_num; i++)
			argv[i + 2] = (char *)cfg->crm.extra_args[i];
113

Johannes Berg's avatar
Johannes Berg committed
114
		debugv(&cfg->dbgcfg, argv);
115

Timo Sirainen's avatar
Timo Sirainen committed
116
		T_BEGIN {
117 118 119 120 121 122 123 124 125 126
		for (i = 0; i < cfg->crm.extra_env_num; i++) {
			char *name, *value;
			name = t_strdup_noconst(cfg->crm.extra_env[i]);
			value = strchr(name, '=');
			if (value) {
				*value = '\0';
				value++;
			}
			setenv(name, value, 1);
		}
Timo Sirainen's avatar
Timo Sirainen committed
127
		} T_END;
128

Johannes Berg's avatar
Johannes Berg committed
129
		execv(cfg->crm.reaver_binary, argv);
130
		/* fall through if reaver can't be found */
Johannes Berg's avatar
Johannes Berg committed
131
		debug(&cfg->dbgcfg, "executing %s failed: %d (uid=%d, gid=%d)",
Johannes Berg's avatar
Johannes Berg committed
132
			cfg->crm.reaver_binary, errno, getuid(), getgid());
133 134 135 136 137 138 139 140 141 142
		exit(127);
		/* not reached */
		return -1;
	}
}

struct antispam_transaction_context {
	struct siglist *siglist;
};

143
static struct antispam_transaction_context *
Johannes Berg's avatar
Johannes Berg committed
144 145
backend_start(const struct antispam_config *cfg ATTR_UNUSED,
	      struct mailbox *box ATTR_UNUSED)
146 147 148 149 150 151 152 153
{
	struct antispam_transaction_context *ast;

	ast = i_new(struct antispam_transaction_context, 1);
	ast->siglist = NULL;
	return ast;
}

Johannes Berg's avatar
Johannes Berg committed
154 155
static void backend_rollback(const struct antispam_config *cfg ATTR_UNUSED,
			     struct antispam_transaction_context *ast)
156 157 158 159 160
{
	signature_list_free(&ast->siglist);
	i_free(ast);
}

Johannes Berg's avatar
Johannes Berg committed
161 162 163
static int backend_commit(const struct antispam_config *cfg,
			  struct mailbox_transaction_context *ctx,
			  struct antispam_transaction_context *ast)
164 165 166 167 168
{
	struct siglist *item = ast->siglist;
	int ret = 0;

	while (item) {
Johannes Berg's avatar
Johannes Berg committed
169
		if (call_reaver(cfg, item->sig, item->wanted)) {
170 171
			ret = -1;
			mail_storage_set_error(ctx->box->storage,
172
					       ME(NOTPOSSIBLE)
173 174 175 176 177 178 179 180 181 182 183
					       "Failed to call reaver");
			break;
		}
		item = item->next;
	}

	signature_list_free(&ast->siglist);
	i_free(ast);
	return ret;
}

Johannes Berg's avatar
Johannes Berg committed
184 185
static int backend_handle_mail(const struct antispam_config *cfg,
			       struct mailbox_transaction_context *t,
186 187
			       struct antispam_transaction_context *ast,
			       struct mail *mail, enum classification want)
188
{
Johannes Berg's avatar
Johannes Berg committed
189
	return signature_extract_to_list(&cfg->crm.sigcfg, t, mail, &ast->siglist, want);
190 191
}

Johannes Berg's avatar
Johannes Berg committed
192 193 194
static void backend_init(struct antispam_config *cfg,
			 const char *(getenv)(const char *env, void *data),
			 void *getenv_data)
195
{
Johannes Berg's avatar
Johannes Berg committed
196
	const char *tmp;
197 198
	int i;

Johannes Berg's avatar
Johannes Berg committed
199
	tmp = getenv("CRM_BINARY", getenv_data);
200
	if (tmp) {
Johannes Berg's avatar
Johannes Berg committed
201
		cfg->crm.reaver_binary = tmp;
Johannes Berg's avatar
Johannes Berg committed
202
		debug(&cfg->dbgcfg, "reaver binary set to %s\n", tmp);
Johannes Berg's avatar
Johannes Berg committed
203 204
	} else
		cfg->crm.reaver_binary = "/bin/false";
205

Johannes Berg's avatar
Johannes Berg committed
206
	tmp = getenv("CRM_ARGS", getenv_data);
207
	if (tmp) {
Johannes Berg's avatar
Johannes Berg committed
208 209 210 211
		cfg->crm.extra_args = p_strsplit(cfg->mem_pool, tmp, ";");
		cfg->crm.extra_args_num = str_array_length(
					(const char *const *)cfg->crm.extra_args);
		for (i = 0; i < cfg->crm.extra_args_num; i++)
Johannes Berg's avatar
Johannes Berg committed
212
			debug(&cfg->dbgcfg, "reaver extra arg %s\n",
Johannes Berg's avatar
Johannes Berg committed
213
			      cfg->crm.extra_args[i]);
214 215
	}

216 217 218 219 220 221 222 223 224 225
	tmp = getenv("CRM_ENV", getenv_data);
	if (tmp) {
		cfg->crm.extra_env = p_strsplit(cfg->mem_pool, tmp, ";");
		cfg->crm.extra_env_num = str_array_length(
					(const char *const *)cfg->crm.extra_env);
		for (i = 0; i < cfg->crm.extra_env_num; i++)
			debug(&cfg->dbgcfg, "reaver env %s\n",
			      cfg->crm.extra_env[i]);
	}

Johannes Berg's avatar
Johannes Berg committed
226
	signature_init(&cfg->crm.sigcfg, &cfg->dbgcfg, getenv, getenv_data);
227
}
228

229
struct backend crm114_backend = {
230 231 232 233 234
	.init = backend_init,
	.handle_mail = backend_handle_mail,
	.start = backend_start,
	.rollback = backend_rollback,
	.commit = backend_commit,
235
};