// // Copyright (c) Victor Ustugov 2006-2008 // http://mta.org.ua/exim-4.68-conf/dlfunc/kav4lms/kav4lms.c // License: GPLv2 // // // based on examples from http://www.ols.es/exim/dlext/ by David Saez , // source code of exim by Philip Hazel , // source code of exiscan by Tom Kistner // and source code of kav4lms exiscan by Kaspersky Lab. // // // warn set acl_m0 = ${dlfunc{/usr/local/libexec/exim/exim-dlfunc.so}{kav4lms}\ // {local:/var/run/kav4lms/kavmd.sock}{defer_ok}} // // warn set acl_m0 = ${dlfunc{/usr/local/libexec/exim/exim-dlfunc.so}{kav4lms}\ // {inet:1025@127.0.0.1}{defer_ok}} // //#include "local_scan.h" //#include "macros.h" #include "exim.h" #include #include #include #include #include #include #include #include "kaverrors.h" #include "kavclientstub.h" #include "kavcommand.h" //extern BOOL split_spool_directory; /* TRUE to use multiple subdirs */ //extern uschar *spool_directory; /* Name of spool directory */ //extern uschar message_subdir[]; /* Subdirectory for messages */ #define MAX_TMP_CREATE_FAILS 10 const char temp_dir[] = "/var/spool/exim/kav4lms"; //------------------------------------------------------------------------- int kav4lms(uschar **yield, int argc, uschar *argv[]) { char *arg_socket_addr; char *arg_defer_ok; int defer_ok; char spool_path[512]; FILE *spool_file = NULL; char mbox_path[512]; int mbox_file = -1; int mbox_created = 0; static int miRand = 1; int offset, read_count, wrote_count; header_line *my_header; uschar *s, *p; #ifndef NO_POLL_H int result; struct pollfd pollfd; #endif uschar kav4lms_buffer[32600]; int i; struct kavHeaderList *headers = 0; char *rejectMsg = 0; int res; arg_socket_addr = argv[0]; arg_defer_ok = argv[1]; if (argc < 2) { defer_ok = 0; } else if ( (strcmpic(arg_defer_ok,US"1") == 0) || (strcmpic(arg_defer_ok,US"yes") == 0) || (strcmpic(arg_defer_ok,US"true") == 0) || (strcmpic(arg_defer_ok,US"defer_ok") == 0) ) { defer_ok = 1; } else { defer_ok = 0; } debug_printf("kav4lms dlfunc: defer_ok: %d\n", defer_ok); if (argc < 2) { log_write(0, LOG_MAIN|LOG_PANIC, "kav4lms dlfunc: Bad number of arguments: %d", argc); *yield = string_sprintf("kav4lms dlfunc: Bad number of arguments: %d", argc); goto RETURN_DEFER; } if ((arg_socket_addr == NULL) || (arg_socket_addr[0] == 0)) { log_write(0, LOG_MAIN|LOG_PANIC, "kav4lms dlfunc: Socket address expected"); *yield = string_sprintf("kav4lms dlfunc: Socket address expected"); goto RETURN_DEFER; } // // get message body stream // if (split_spool_directory == 0) { sprintf(spool_path, "%s/input/%s-D", spool_directory, message_id); } else { sprintf(spool_path, "%s/input/%s/%s-D", spool_directory, message_subdir, message_id); } debug_printf("Open spool file: %s\n", spool_path); spool_file = fopen(spool_path, "rb"); if (!spool_file) { log_write(0, LOG_MAIN|LOG_PANIC, "kav4lms dlfunc: Unable to spool message"); *yield = string_copy((uschar *)"kav4lms dlfunc: DEFER: Unable to spool message"); goto RETURN_DEFER; } debug_printf("fseek %d, %d\n", SPOOL_DATA_START_OFFSET, SEEK_SET); (void)fseek(spool_file, SPOOL_DATA_START_OFFSET, SEEK_SET); // // create temporary file // for (i = 0; i < MAX_TMP_CREATE_FAILS; i++) { sprintf(mbox_path, "%s/%s-%8.8d-%3.3d-%6.6d", temp_dir, message_id, getpid(), miRand++, time(NULL)); umask ((mode_t) 0); debug_printf("Create temporary file %s\n", mbox_path); if ((mbox_file = open(mbox_path, O_RDWR | O_EXCL | O_CREAT, 0640)) > 0) break; if (errno != EEXIST) { // Unexpected error debug_printf("Unable create temporary file: %d\n", errno); log_write(0, LOG_MAIN|LOG_PANIC, "kav4lms dlfunc: Unable create temporary file %s: %d", mbox_path, errno); *yield = string_sprintf("kav4lms dlfunc: DEFER: Unable create temporary file %s: %d", mbox_path, errno); goto RETURN_DEFER; } // Else, for some reason this file exists. Try again after a short sleep sleep(1); } if (mbox_file <= 0) { debug_printf("Unable create temporary file\n"); log_write(0, LOG_MAIN|LOG_PANIC, "kav4lms dlfunc: Unable create temporary file %s", mbox_path); *yield = string_sprintf("kav4lms dlfunc: DEFER: Unable create temporary file %s", mbox_path); goto RETURN_DEFER; } // // Write message headers to temporary file // for (my_header = header_list; my_header; my_header = my_header->next) { if ((my_header->type != '*') && (my_header->type != htype_old)) { wrote_count = write(mbox_file, my_header->text, my_header->slen); if (wrote_count == -1) goto FILE_WRITE_FAILED; } } wrote_count = write(mbox_file, "\n", 1); if (wrote_count == -1) goto FILE_WRITE_FAILED; // // Write message body to temporary file // do { read_count = fread(kav4lms_buffer, 1, sizeof(kav4lms_buffer) - 1, spool_file); kav4lms_buffer[read_count] = 0; //debug_printf("Read %d bytes from file: %s", read_count, kav4lms_buffer); debug_printf("Read %d bytes from spool file\n", read_count); if (read_count > 0) { offset = 0; //debug_printf("Write %d bytes to temporary file\n", wrote_count, kav4lms_buffer); again: wrote_count = write(mbox_file, kav4lms_buffer + offset, read_count - offset); debug_printf("Wrote %d bytes to temporary file\n", wrote_count); if (wrote_count == -1) goto FILE_WRITE_FAILED; if (offset + wrote_count < read_count) { offset += wrote_count; goto again; } } } while (!feof(spool_file) && !ferror(spool_file)); if (ferror(spool_file)) { log_write(0, LOG_MAIN|LOG_PANIC, "kav4lms dlfunc: error reading spool file: %s", strerror(errno)); *yield = string_sprintf("kav4lms dlfunc: error reading spool file: %s", strerror(errno)); goto RETURN_DEFER; } debug_printf("Close spool file: %s\n", spool_path); (void)fclose(spool_file); spool_file = NULL; // // Save temporary file // debug_printf("Save temporary file: %s\n", mbox_path); (void)close(mbox_file); mbox_file = -1; mbox_created = 1; memset(&kav4lms_buffer, '\0', sizeof(kav4lms_buffer)); // request for scan res = kavProcess(mbox_path, arg_socket_addr, expand_string(US"${sender_address}"), ((argc >= 3) && (argv[2] != NULL) && (strlen(argv[2]) > 0) ? argv[2] : expand_string(US"${recipients}")), expand_string(US"${message_exim_id}"), expand_string(US"${sender_host_address}"), expand_string(US"${received_ip_address}"), &headers, &rejectMsg); debug_printf("kav4lms dlfunc: Scan returned %d\n", res); if (rejectMsg) debug_printf("kav4lms dlfunc: rejectMsg: %s\n", rejectMsg); if (res == -1) debug_printf("kav4lms dlfunc: error message: %s\n", (uschar *) kavGetError()); if (res & CMD_MTA_ACTION_TEMPFAIL) { if (res == -1) { s = string_sprintf("DEFER: %s\n", (uschar *) kavGetError()); } else { s = string_sprintf("DEFER\n"); } debug_printf("Write to temporary buffer: %s", s); Ustrcpy(kav4lms_buffer + Ustrlen(kav4lms_buffer), s); } else if (res & CMD_MTA_ACTION_DROP) { s = string_sprintf("DROP\n"); debug_printf("Write to temporary buffer: %s", s); Ustrcpy(kav4lms_buffer + Ustrlen(kav4lms_buffer), s); } else if (res & CMD_MTA_ACTION_REJECT) { if (rejectMsg) { s = string_sprintf("REJECT: %s\n", rejectMsg); } else { s = string_sprintf("REJECT\n"); } debug_printf("Write to temporary buffer: %s", s); Ustrcpy(kav4lms_buffer + Ustrlen(kav4lms_buffer), s); } else { s = string_sprintf("ACCEPT\n"); debug_printf("Write to temporary buffer: %s", s); Ustrcpy(kav4lms_buffer + Ustrlen(kav4lms_buffer), s); } s = string_sprintf("temporary file %s\n", mbox_path); debug_printf("Write to temporary buffer: %s", s); Ustrcpy(kav4lms_buffer + Ustrlen(kav4lms_buffer), s); { struct kavHeaderList *it; for (it = headers; it; it = it->next) { char *name = 0; char *value = 0; // check if last char is '\n' if (it->data.data[it->data.slen - 1] == '\n') { // make a copy of the data, and add a NUL terminator value = malloc(it->data.slen + 1); strncpy(value, it->data.data, it->data.slen); value[it->data.slen] = 0; } else { // make a copy of the data, and add '\n' + NUL terminator value = malloc(it->data.slen + 2); strncpy(value, it->data.data, it->data.slen); value[it->data.slen] = '\n'; value[it->data.slen+1] = 0; } if (value == 0) { log_write(0, LOG_MAIN|LOG_PANIC, "kav4lms dlfunc: Cannot allocate memory"); *yield = string_sprintf("kav4lms dlfunc: Cannot allocate memory"); goto RETURN_DEFER; } debug_printf("Write to temporary buffer: %s", value); Ustrcpy(kav4lms_buffer + Ustrlen(kav4lms_buffer), value); name = strsep(&value, ":"); debug_printf("kav4lms dlfunc: header name is '%s', value is '%s'\n", name, value); free(name); // yes, name not value, due to strsep() call } } if (mbox_created > 0) { debug_printf("Remove temporary file %s\n", mbox_path); unlink(mbox_path); mbox_created = 0; } *yield = string_sprintf("KAV4LMS answer: %s", kav4lms_buffer); return OK; FILE_WRITE_FAILED: { log_write(0, LOG_MAIN|LOG_PANIC, "kav4lms dlfunc: Unable to write to temporary file %s", mbox_path); *yield = string_sprintf("kav4lms dlfunc: DEFER: Unable to write to temporary file %s", mbox_path); goto RETURN_DEFER; } RETURN_DEFER: { if (spool_file > 0) (void)fclose(spool_file); if (mbox_file > 0) { (void)close(mbox_file); mbox_file = -1; } if (mbox_created > 0) { debug_printf("Remove temporary file %s\n", mbox_path); unlink(mbox_path); mbox_created = 0; } if ((*yield == NULL) || (*yield[0] == '\0')) *yield = string_sprintf("so dlfunc: DEFER"); return(defer_ok ? OK : ERROR); } return OK; }