int
main (int argc, char *argv[])
{
    gint status = MI_SUCCESS;
    GError *error = NULL;
    GOptionContext *option_context;
    unsigned int major, minor, patch_level;

#ifdef HAVE_LOCALE_H
    setlocale(LC_ALL, "");
#endif

    /*
     * workaround for memory profiler for GLib memory
     * profiler, we need to call g_mem_set_vtable prior to
     * any other GLib functions. smfi_version() calls
     * g_mem_set_vtable() internally.
     */
    smfi_version(&major, &minor, &patch_level);

    option_context = g_option_context_new(NULL);
    g_option_context_add_main_entries(option_context, option_entries, NULL);

    if (!g_option_context_parse(option_context, &argc, &argv, &error)) {
        g_print("%s\n", error->message);
        g_error_free(error);
        g_option_context_free(option_context);
        exit(EXIT_FAILURE);
    }

    if (verbose)
        smfi_setdbg(6);

    status = smfi_setconn(spec);
    if (status == MI_SUCCESS)
        status = smfi_register(smfilter);
    if (status == MI_SUCCESS)
        return smfi_main() == MI_SUCCESS ? EXIT_SUCCESS : EXIT_FAILURE;

    return EXIT_FAILURE;
}
Esempio n. 2
0
main(int argc, char **argv)
{
	int	sts ;

//	Set up the Milter socket
	unlink(MILTER_SOCKET) ;
	smfi_setconn(MILTER_SOCKET) ;

	openlog(PACKAGE_NAME, LOG_PID, LOG_MAIL) ;
	syslog(LOG_INFO, "%s Initializing...", PACKAGE_STRING) ;

//	Register our milter callbacks
	if(smfi_register(smilter) == MI_FAILURE) {
		fprintf(stderr, "%s: smfi_register failed\n", argv[0]) ;
		exit(EX_UNAVAILABLE) ;
	}

//	And away we go!
	sts = smfi_main() ;
	closelog() ;
	return sts ;
}	
Esempio n. 3
0
int main(int argc, char **argv) {
    char *my_socket, *pt;
    const struct optstruct *opt;
    struct optstruct *opts;
    time_t currtime;
    mode_t umsk;
    int ret;

    cl_initialize_crypto();

    memset(&descr, 0, sizeof(struct smfiDesc));
    descr.xxfi_name = "ClamAV";			/* filter name */
    descr.xxfi_version = SMFI_VERSION;		/* milter version */
    descr.xxfi_flags = SMFIF_QUARANTINE;	/* flags */
    descr.xxfi_connect = clamfi_connect;	/* connection info filter */
    descr.xxfi_envfrom = clamfi_envfrom;	/* envelope sender filter */
    descr.xxfi_envrcpt = clamfi_envrcpt;	/* envelope recipient filter */
    descr.xxfi_header = clamfi_header;		/* header filter */
    descr.xxfi_body = clamfi_body;		/* body block */
    descr.xxfi_eom = clamfi_eom;		/* end of message */
    descr.xxfi_abort = clamfi_abort;		/* message aborted */

    opts = optparse(NULL, argc, argv, 1, OPT_MILTER, 0, NULL);
    if (!opts) {
	mprintf("!Can't parse command line options\n");
	return 1;
    }

    if(optget(opts, "help")->enabled) {
	printf("Usage: %s [-c <config-file>]\n\n", argv[0]);
	printf("    --help                   -h       Show this help\n");
	printf("    --version                -V       Show version and exit\n");
	printf("    --config-file <file>     -c       Read configuration from file\n\n");
	optfree(opts);
	return 0;
    }

    if(opts->filename) {
	int x;
	for(x = 0; opts->filename[x]; x++)
	    mprintf("^Ignoring option %s\n", opts->filename[x]);
    }

    if(optget(opts, "version")->enabled) {
	printf("clamav-milter %s\n", get_version());
	optfree(opts);
	return 0;
    }

    pt = strdup(optget(opts, "config-file")->strarg);
    if (pt == NULL) {
	printf("Unable to allocate memory for config file\n");
	return 1;
    }
    if((opts = optparse(pt, 0, NULL, 1, OPT_MILTER, 0, opts)) == NULL) {
	printf("%s: cannot parse config file %s\n", argv[0], pt);
	free(pt);
	return 1;
    }
    free(pt);

    if((opt = optget(opts, "Chroot"))->enabled) {
	if(chdir(opt->strarg) != 0) {
	    logg("!Cannot change directory to %s\n", opt->strarg);
	    return 1;
	}
	if(chroot(opt->strarg) != 0) {
	    logg("!chroot to %s failed. Are you root?\n", opt->strarg);
	    return 1;
	}
    }

    pt = optget(opts, "AddHeader")->strarg;
    if (strcasecmp(pt, "No")) {
	char myname[255];

	if (((opt = optget(opts, "ReportHostname"))->enabled &&
	     strncpy(myname, opt->strarg, sizeof(myname))) ||
	    !gethostname(myname, sizeof(myname))) {

	    myname[sizeof(myname)-1] = '\0';
	    snprintf(xvirushdr, sizeof(xvirushdr), "clamav-milter %s at %s",
		     get_version(), myname);
	} else {
	    snprintf(xvirushdr, sizeof(xvirushdr), "clamav-milter %s",
		     get_version());
	}
	xvirushdr[sizeof(xvirushdr)-1] = '\0';

	descr.xxfi_flags |= SMFIF_ADDHDRS;

	if (strcasecmp(pt, "Add")) { /* Replace or Yes */
	    descr.xxfi_flags |= SMFIF_CHGHDRS;
	    addxvirus = 1;
	} else { /* Add */
	    addxvirus = 2;
	}
    }

    if(!(my_socket = optget(opts, "MilterSocket")->strarg)) {
	logg("!Please configure the MilterSocket directive\n");
	logg_close();
	optfree(opts);
	return 1;
    }

    if(smfi_setconn(my_socket) == MI_FAILURE) {
	logg("!smfi_setconn failed\n");
	logg_close();
	optfree(opts);
	return 1;
    }
    if(smfi_register(descr) == MI_FAILURE) {
	logg("!smfi_register failed\n");
	logg_close();
	optfree(opts);
	return 1;
    }
    opt = optget(opts, "FixStaleSocket");
    umsk = umask(0777); /* socket is created with 000 to avoid races */
    if(smfi_opensocket(opt->enabled) == MI_FAILURE) {
	logg("!Failed to create socket %s\n", my_socket);
	logg_close();
	optfree(opts);
	return 1;
    }
    umask(umsk); /* restore umask */
    if(strncmp(my_socket, "inet:", 5) && strncmp(my_socket, "inet6:", 6)) {
	/* set group ownership and perms on the local socket */
	char *sock_name = my_socket;
	mode_t sock_mode;
	if(!strncmp(my_socket, "unix:", 5))
	    sock_name += 5;
	if(!strncmp(my_socket, "local:", 6))
	    sock_name += 6;
	if(*my_socket == ':')
	    sock_name ++;

	if(optget(opts, "MilterSocketGroup")->enabled) {
	    char *gname = optget(opts, "MilterSocketGroup")->strarg, *end;
	    gid_t sock_gid = strtol(gname, &end, 10);
	    if(*end) {
		struct group *pgrp = getgrnam(gname);
		if(!pgrp) {
		    logg("!Unknown group %s\n", gname);
		    logg_close();
		    optfree(opts);
		    return 1;
		}
		sock_gid = pgrp->gr_gid;
	    }
	    if(chown(sock_name, -1, sock_gid)) {
		logg("!Failed to change socket ownership to group %s\n", gname);
		logg_close();
		optfree(opts);
		return 1;
	    }
	}

	if ((opt = optget(opts, "User"))->enabled) {
	    struct passwd *user;
	    if ((user = getpwnam(opt->strarg)) == NULL) {
		logg("ERROR: Can't get information about user %s.\n",
			opt->strarg);
		logg_close();
		optfree(opts);
		return 1;
	    }

	    if(chown(sock_name, user->pw_uid, -1)) {
		logg("!Failed to change socket ownership to user %s\n", user->pw_name);
		optfree(opts);
		logg_close();
		return 1;
	    }
	}

	if(optget(opts, "MilterSocketMode")->enabled) {
	    char *end;
	    sock_mode = strtol(optget(opts, "MilterSocketMode")->strarg, &end, 8);
	    if(*end) {
		logg("!Invalid MilterSocketMode %s\n", optget(opts, "MilterSocketMode")->strarg);
		logg_close();
		optfree(opts);
		return 1;
	    }
	} else
	    sock_mode = 0777 & ~umsk;

	if(chmod(sock_name, sock_mode & 0666)) {
	    logg("!Cannot set milter socket permission to %s\n", optget(opts, "MilterSocketMode")->strarg);
	    logg_close();
	    optfree(opts);
	    return 1;
	}
    }

    if(geteuid() == 0 && (opt = optget(opts, "User"))->enabled) {
        struct passwd *user = NULL;
	if((user = getpwnam(opt->strarg)) == NULL) {
	    fprintf(stderr, "ERROR: Can't get information about user %s.\n", opt->strarg);
	    optfree(opts);
	    return 1;
	}

	if(optget(opts, "AllowSupplementaryGroups")->enabled) {
#ifdef HAVE_INITGROUPS
	    if(initgroups(opt->strarg, user->pw_gid)) {
		fprintf(stderr, "ERROR: initgroups() failed.\n");
		optfree(opts);
		return 1;
	    }
#else
	    mprintf("!AllowSupplementaryGroups: initgroups() is not available, please disable AllowSupplementaryGroups\n");
	    optfree(opts);
	    return 1;
#endif
	} else {
#ifdef HAVE_SETGROUPS
	    if(setgroups(1, &user->pw_gid)) {
		fprintf(stderr, "ERROR: setgroups() failed.\n");
		optfree(opts);
		return 1;
	    }
#endif
	}

	if(setgid(user->pw_gid)) {
	    fprintf(stderr, "ERROR: setgid(%d) failed.\n", (int) user->pw_gid);
	    optfree(opts);
	    return 1;
	}

	if(setuid(user->pw_uid)) {
	    fprintf(stderr, "ERROR: setuid(%d) failed.\n", (int) user->pw_uid);
	    optfree(opts);
	    return 1;
	}
    }

    logg_lock = !optget(opts, "LogFileUnlock")->enabled;
    logg_time = optget(opts, "LogTime")->enabled;
    logg_size = optget(opts, "LogFileMaxSize")->numarg;
    logg_verbose = mprintf_verbose = optget(opts, "LogVerbose")->enabled;
    if (logg_size)
        logg_rotate = optget(opts, "LogRotate")->enabled;

    if((opt = optget(opts, "LogFile"))->enabled) {
	logg_file = opt->strarg;
	if(!cli_is_abspath(logg_file)) {
	    fprintf(stderr, "ERROR: LogFile requires full path.\n");
	    logg_close();
	    optfree(opts);
	    return 1;
	}
    } else
	logg_file = NULL;

#if defined(USE_SYSLOG) && !defined(C_AIX)
    if(optget(opts, "LogSyslog")->enabled) {
	int fac;

	opt = optget(opts, "LogFacility");
	if((fac = logg_facility(opt->strarg)) == -1) {
	    logg("!LogFacility: %s: No such facility.\n", opt->strarg);
	    logg_close();
	    optfree(opts);
	    return 1;
	}

	openlog("clamav-milter", LOG_PID, fac);
	logg_syslog = 1;
    }
#endif

    time(&currtime);
    if(logg("#+++ Started at %s", ctime(&currtime))) {
	fprintf(stderr, "ERROR: Can't initialize the internal logger\n");
	logg_close();
	optfree(opts);
	return 1;
    }
    if((opt = optget(opts, "TemporaryDirectory"))->enabled)
	tempdir = opt->strarg;

    if(localnets_init(opts) || init_actions(opts)) {
	logg_close();
	optfree(opts);
	return 1;
    }

    if((opt = optget(opts, "Whitelist"))->enabled && whitelist_init(opt->strarg)) {
	localnets_free();
	logg_close();
	optfree(opts);
	return 1;
    }

    if((opt = optget(opts, "SkipAuthenticated"))->enabled && smtpauth_init(opt->strarg)) {
	localnets_free();
	whitelist_free();
	logg_close();
	optfree(opts);
	return 1;
    }

    multircpt = optget(opts, "SupportMultipleRecipients")->enabled;
    
    if(!optget(opts, "Foreground")->enabled) {
	if(daemonize() == -1) {
	    logg("!daemonize() failed\n");
	    localnets_free();
	    whitelist_free();
	    cpool_free();
	    logg_close();
	    optfree(opts);
	    return 1;
	}
	if(chdir("/") == -1)
	    logg("^Can't change current working directory to root\n");
    }

    maxfilesize = optget(opts, "MaxFileSize")->numarg;
    if(!maxfilesize) {
	logg("^Invalid MaxFileSize, using default (%d)\n", CLI_DEFAULT_MAXFILESIZE);
	maxfilesize = CLI_DEFAULT_MAXFILESIZE;
    }
    readtimeout = optget(opts, "ReadTimeout")->numarg;

    cpool_init(opts);
    if (!cp) {
	logg("!Failed to init the socket pool\n");
	localnets_free();
	whitelist_free();
	logg_close();
	optfree(opts);
	return 1;
    }	

    if((opt = optget(opts, "PidFile"))->enabled) {
	FILE *fd;
	mode_t old_umask = umask(0002);

	if((fd = fopen(opt->strarg, "w")) == NULL) {
	    logg("!Can't save PID in file %s\n", opt->strarg);
	} else {
	    if (fprintf(fd, "%u\n", (unsigned int)getpid())<0) {
	    	logg("!Can't save PID in file %s\n", opt->strarg);
	    }
	    fclose(fd);
	}
	umask(old_umask);
    }

    ret = smfi_main();

    optfree(opts);

    logg_close();
    cpool_free();
    localnets_free();
    whitelist_free();

    return ret;
}
Esempio n. 4
0
File: bav.c Progetto: dpalic/openemm
	/*}}}*/
};

static int
usage (const char *pgm) /*{{{*/
{
	fprintf (stderr, "Usage: %s [-L <loglevel>] [-s <socket name>] [-s <reread in seconds>] [-c <config filename>] [-l]\n", pgm);
	return 1;
}/*}}}*/
int
main (int argc, char **argv) /*{{{*/
{
	int	rc;
	char	*ptr;
	char	*sock_name;
	int	reread;
	int	n;
	lock_t	*lock;

	if (ptr = strrchr (argv[0], '/'))
		program = ptr + 1;
	else
		program = argv[0];
	loglevel = "WARNING";
	sock_name = NULL;
	reread = 0;
	cfgfile = NULL;

	while ((n = getopt (argc, argv, "L:s:r:c:l")) != -1)
		switch (n) {
		case 'L':
			loglevel = optarg;
			break;
		case 's':
			if (sock_name)
				free (sock_name);
			if (! (sock_name = strdup (optarg))) {
				fprintf (stderr, "Failed to allocate memory for socket name %s (%m).\n", optarg);
				return 1;
			}
			break;
		case 'r':
			if ((reread = atoi (optarg)) < 1) {
				fprintf (stderr, "Reread value must be at least 1.\n");
				return 1;
			}
			break;
		case 'c':
			if (cfgfile)
				free (cfgfile);
			if (! (cfgfile = strdup (optarg))) {
				fprintf (stderr, "Failed to allocate memory for config.filename %s (%m).\n", optarg);
				return 1;
			}
			break;
		case 'l':
			break;
		default:
			return usage (argv[0]);
		}
	if (optind < argc)
		return usage (argv[0]);
	if (! (lock = lock_alloc (LOCK_PATH))) {
		fprintf (stderr, "Failed to allocate memory for locking (%m).\n");
		return 1;
	}
	if (! lock_lock (lock)) {
		fprintf (stderr, "Instance seems already to run, aborting.\n");
		return 1;
	}

	if ((! sock_name) || (! cfgfile)) {
		const char	*home;

		if (! (home = getenv ("HOME")))
			home = "";
		if (! sock_name) {
			if (! (sock_name = malloc (strlen (home) + sizeof (SOCK_PATH) + 16))) {
				fprintf (stderr, "Failed to allocate socket name %s/%s (%m).\n", home, SOCK_PATH);
				return 1;
			}
			sprintf (sock_name, "unix:%s/%s", home, SOCK_PATH);
		}
		if (! cfgfile) {
			if (! (cfgfile = malloc (strlen (home) + sizeof (CFGFILE) + 1))) {
				fprintf (stderr, "Failed to allocate config.filename %s/%s (%m).\n", home, CFGFILE);
				return 1;
			}
			sprintf (cfgfile, "%s/%s", home, CFGFILE);
		}
	}
	rc = 1;
	umask (0);
	if (smfi_register (bav) == MI_FAILURE)
		fprintf (stderr, "Failed to register filter.\n");
	else if (smfi_setconn (sock_name) == MI_FAILURE)
		fprintf (stderr, "Failed to register socket name \"%s\".\n", sock_name);
	else if (smfi_opensocket (1) == MI_FAILURE)
		fprintf (stderr, "Failed to open socket socket \"%s\".\n", sock_name);
	else {
		if (smfi_main () == MI_FAILURE)
			fprintf (stderr, "Failed to hand over control to milter.\n");
		else
			rc = 0;
	}
	unlink (sock_name);
	lock_unlock (lock);
	lock_free (lock);
	free (sock_name);
	free (cfgfile);
	return rc;
}/*}}}*/
Esempio n. 5
0
int
main(int argc, char **argv)
{
	const char *progname = argv[0];
	const char *port;
	const char *mail_log = DEFAULT_MAIL_LOG;
	int c;
	pthread_t tid;
	struct smfiDesc smfilter = {
		"blacklist", /* filter name */
		SMFI_VERSION,	/* version code -- leave untouched */
		SMFIF_NONE,	/* flags - we only look */
		blacklist_connect, /* connection callback */
		NULL,		/* HELO filter callback */
		NULL,	/* envelope sender filter callback */
		NULL,	/* envelope recipient filter callback */
		blacklist_header, /* header filter callback */
		NULL,	/* end of header callback */
		blacklist_body,	/* body filter callback */
		NULL,	/* end of message callback */
		blacklist_cleanup,	/* message aborted callback */
		blacklist_cleanup,	/* connection cleanup callback */
#if	SMFI_VERSION > 2
		NULL,		/* Unrecognised command */
#endif
#if	SMFI_VERSION > 3
		NULL,		/* DATA command callback */
#endif
#if	SMFI_VERSION >= 0x01000000
		NULL,		/* Negotiation callback */
#endif
	};

	while((c = getopt(argc, argv, "f:")) != EOF)
		switch(c) {
			case 'f':
				mail_log = optarg;
				break;
			default:
				fprintf(stderr, "Usage: %s [ -f log_file ] socket-addr\n", progname);
				return EX_USAGE;
		}

	if(optind >= argc) {
		fprintf(stderr, "Usage: %s [ -f log_file ] socket-addr\n", progname);
		return EX_USAGE;
	}

	if(access(mail_log, R_OK) < 0) {
		perror(mail_log);
		return EX_NOINPUT;
	}

	openlog("blacklist-milter", LOG_CONS|LOG_PID, LOG_MAIL);

#ifndef	DEBUG
	switch(fork()) {
		case -1:
			perror("fork");
			return EX_OSERR;
		case 0:	/* child */
			break;
		default:	/* parent */
			return EX_OK;
	}
#endif

	port = argv[optind];
	if(strncasecmp(port, "unix:", 5) == 0) {
		if(unlink(&port[5]) < 0)
			if(errno != ENOENT)
				perror(&port[5]);
	} else if(strncasecmp(port, "local:", 6) == 0) {
		if(unlink(&port[6]) < 0)
			if(errno != ENOENT)
				perror(&port[6]);
	} else if(port[0] == '/') {
		if(unlink(port) < 0)
			if(errno != ENOENT)
				perror(port);
	}
	/* sendmail->milter comms */
	if(smfi_setconn(port) == MI_FAILURE) {
		fprintf(stderr, "%s: smfi_setconn failed\n", progname);
		return EX_SOFTWARE;
	}

	if(smfi_register(smfilter) == MI_FAILURE) {
		fprintf(stderr, "%s: smfi_register failed\n", progname);
		return EX_UNAVAILABLE;
	}

	if(smfi_opensocket(1) == MI_FAILURE) {
		fprintf(stderr, "%s: can't open/create %s\n", progname, port);
		return EX_CONFIG;
	}

	syslog(LOG_INFO, "Starting blacklist");

#ifndef	DEBUG
	close(0);
	close(1);
	close(2);
	open("/dev/null", O_RDONLY);
	open("/dev/console", O_WRONLY);
	if(dup(1) < 0) {
		perror("dup");
		return EX_OSERR;
	}
#endif

	setpgrp();

	pthread_create(&tid, NULL, watchdog, mail_log);

	return smfi_main();
}
Esempio n. 6
0
int main(int argc, char *argv[])
{
	int c, r;
	extern int yynerrs;
	extern FILE *yyin;
	const char *args = "c:hndv";
	char *cfg_file = NULL;
	FILE *f;
	pthread_t reload_thr;
	rmilter_pidfh_t *pfh = NULL;
	pid_t pid;

	daemonize = 1;

	/* Process command line options */
	while ((c = getopt (argc, argv, args)) != -1) {
		switch (c) {
		case 'c':
			if (optarg == NULL || *optarg == '\0') {
				fprintf (stderr, "Illegal config_file: %s\n", optarg);
				exit (EX_USAGE);
			}
			else {
				cfg_file = strdup (optarg);
			}
			break;
		case 'n':
			daemonize = 0;
			break;
		case 'd':
			yydebug = 1;
			break;
		case 'v':
			version ();
			break;
		case 'h':
		default:
			usage ();
			break;
		}
	}

	openlog ("rmilter.startup", LOG_PID, LOG_MAIL);

	cfg = (struct config_file*) malloc (sizeof(struct config_file));
	if (cfg == NULL) {
		msg_warn("malloc: %s", strerror (errno));
		return -1;
	}
	bzero (cfg, sizeof(struct config_file));
	init_defaults (cfg);

	if (cfg_file == NULL) {
		cfg_file = strdup ("/usr/local/etc/rmilter.conf");
	}

	f = fopen (cfg_file, "r");
	if (f == NULL) {
		msg_warn("cannot open file: %s", cfg_file);
		return EBADF;
	}
	yyin = f;

	yyrestart (yyin);

	if (yyparse () != 0 || yynerrs > 0) {
		msg_warn("yyparse: cannot parse config file, %d errors", yynerrs);
		return EBADF;
	}

	closelog ();
	openlog (cfg->syslog_name, LOG_PID, LOG_MAIL);

	if (!cfg->cache_use_redis) {
		msg_warn("rmilter is configured to work with legacy memcached cache,"
				" please consider switching to redis by adding "
				"'use_redis = true;' into configuration");
	}

	fclose (f);

	if (argv[0] && strrchr (argv[0], '/') != NULL) {
		_rmilter_progname = strrchr (argv[0], '/') + 1;
	}
	else {
		_rmilter_progname = argv[0];
	}

	cfg->cfg_name = strdup (cfg_file);

	/* Strictly set temp dir */
	if (!cfg->temp_dir) {
		msg_warn("tempdir is not set, trying to use $TMPDIR");
		cfg->temp_dir = getenv ("TMPDIR");

		if (!cfg->temp_dir) {
			cfg->temp_dir = strdup ("/tmp");
		}
	}
	if (cfg->sizelimit == 0) {
		msg_warn("maxsize is not set, no limits on size of scanned mail");
	}

#ifdef HAVE_SRANDOMDEV
	srandomdev();
#else
	srand (time (NULL));
#endif

	umask (0);
	rng_state = get_prng_state ();

	smfi_setconn (cfg->sock_cred);
	if (smfi_register (smfilter) == MI_FAILURE) {
		msg_err("smfi_register failed");
		exit (EX_UNAVAILABLE);
	}

	if (smfi_opensocket (true) == MI_FAILURE) {
		msg_err("Unable to open listening socket");
		exit (EX_UNAVAILABLE);
	}

	if (daemonize && daemon (0, 0) == -1) {
		msg_err("Unable to daemonize");
		exit (EX_UNAVAILABLE);
	}

	msg_info("main: starting rmilter version %s, listen on %s", MVERSION,
			cfg->sock_cred);

	if (pthread_create (&reload_thr, NULL, reload_thread, NULL)) {
		msg_warn("main: cannot start reload thread, ignoring error");
	}

	if (cfg->pid_file) {
		pfh = rmilter_pidfile_open (cfg->pid_file, 0644, &pid);

		if (pfh == NULL) {
			msg_err("Unable to open pidfile %s", cfg->pid_file);
			exit (EX_UNAVAILABLE);
		}

		rmilter_pidfile_write (pfh);
	}

	r = smfi_main ();

	if (cfg_file != NULL)
		free (cfg_file);

	if (pfh) {
		rmilter_pidfile_close (pfh);
	}

	return r;
}
Esempio n. 7
0
int main(int argc, char** argv) {
    int            c;
    char*          p;
    uid_t          uid, gid, client_gid, root_gid;
    struct passwd* pw;
    struct group*  gr;
    struct stat    st;
    int            localsocket = 1;


    /*
     * compilerwarnung: "client_gid may be used uninitialized"
     * sowas will man ja nun wirklich nicht ...
     */
    uid = gid = client_gid = root_gid = 0;

    while ((c = getopt(argc, argv, "bc:d:hfg:k:m:n:s:t:u:vx")) > 0) {
        switch (c) {
        case 'b': /* break contentheader */
            logmsg(LOG_INFO, "option -b is ignored for compatibily reasons, you may remove it safely");
            break;
        case 'c': /* clientgroup */
            opt_clientgroup = optarg;
            if ((gr = getgrnam(opt_clientgroup)) == NULL) {
                logmsg(LOG_ERR, "unknown clientgroup: getgrnam(%s) failed", opt_group);
                exit(EX_DATAERR);
            }
            client_gid = gr->gr_gid;
            break;
        case 'd': /* Loglevel */
            opt_loglevel = (int) strtoul(optarg, &p, 10);
            if (p != NULL && *p != '\0') {
                printf("debug-level is not valid integer: %s\n", optarg);
                exit(EX_DATAERR);
            }
            p = NULL;
            if (opt_loglevel < 0 || opt_loglevel > 7) {
                printf("loglevel out of range 0..7: %i\n", opt_loglevel);
                exit(EX_DATAERR);
            }
            break;
        case 'f': /* signer from header, not from envelope */
            opt_signerfromheader = 1;
            break;
        case 'g': /* group */
            opt_group = optarg;
            if ((gr = getgrnam(opt_group)) == NULL) {
                printf("unknown group: getgrnam(%s) failed", opt_group);
                exit(EX_DATAERR);
            }
            break;
        case 'k': /* keepdir */
            opt_keepdir = optarg;
            if (stat(opt_keepdir, &st) < 0) {
                printf("directory to keep data: %s: %s", opt_keepdir, strerror(errno));
                exit(EX_DATAERR);
            }
            if (!S_ISDIR(st.st_mode)) {
                printf("directory to keep data: %s is not a directory", opt_keepdir);
                exit(EX_DATAERR);
            }
            /* Zugriffsrechte werden spaeter geprueft, wenn zur richtigen uid gewechselt wurde */
            break;
        case '?': /* help */
        case 'h':
            usage();
            exit(EX_OK);
        case 'm': /* Signingtable cdbfilename */
            opt_signingtable = optarg;
            break;
        case 'n': /* Modetable cdbfilename */
            opt_modetable = optarg;
            break;
        case 's': /* Miltersocket */
            opt_miltersocket = optarg;
            break;
        case 't': /* Timeout */
            opt_timeout = (int) strtoul(optarg, &p, 10);
            if (p != NULL && *p != '\0') {
                printf("timeout is not valid integer: %s\n", optarg);
                exit(EX_DATAERR);
            }
            p = NULL;
            if (opt_timeout < 0 ) {
                printf("negative milter connection timeout: %i\n", opt_timeout);
                exit(EX_DATAERR);
            }
            break;
        case 'u': /* user */
            opt_user = optarg;
            /* get passwd/group entries for opt_user and opt_group */
            if ((pw = getpwnam(opt_user)) == NULL) {
                logmsg(LOG_ERR, "unknown user: getpwnam(%s) failed", opt_user);
                exit(EX_DATAERR);
            }
            break;
        case 'v': /* Version */
            version();
            exit(EX_OK);
        case 'x': /* add X-Header */
            opt_addxheader = (int) !opt_addxheader;
            break;
        default:
            usage();
            exit(EX_USAGE);
        }
    }

    /* open syslog an say helo */
    openlog(STR_PROGNAME, LOG_PID, LOG_MAIL);
    logmsg(LOG_NOTICE, "starting %s %s listening on %s, loglevel %i",
               STR_PROGNAME, STR_PROGVERSION, opt_miltersocket, opt_loglevel);

    /* force a new processgroup */
    if ((setsid() == -1))
        logmsg(LOG_DEBUG, "ignoring that setsid() failed");

    if (opt_timeout > 0 && smfi_settimeout(opt_timeout) != MI_SUCCESS) {
        logmsg(LOG_ERR, "could not set milter timeout");
        exit(EX_SOFTWARE);
    }
    logmsg(LOG_INFO, "miltertimeout set to %i", opt_timeout);

    if (smfi_setconn(opt_miltersocket) != MI_SUCCESS) {
        logmsg(LOG_ERR, "could not set milter socket");
        exit(EX_SOFTWARE);
    }

    if (smfi_register(callbacks) != MI_SUCCESS) {
        logmsg(LOG_ERR, "could not register milter");
        exit(EX_SOFTWARE);
    }

    /*
     * User- und Gruppennamen stehen nun fest. Testen, ob es diese gibt
     * und uid / gid ermitteln
     */
    if ((pw = getpwnam(opt_user)) == NULL) {
        logmsg(LOG_ERR, "unknown user: getpwnam(%s) failed", opt_user);
        exit(EX_DATAERR);
    }
    uid = pw->pw_uid;
    if ((gr = getgrnam(opt_group)) == NULL) {
        logmsg(LOG_ERR, "unknown group: getgrnam(%s) failed", opt_group);
        exit(EX_SOFTWARE);
    }
    gid = gr->gr_gid;

    /* wenn nicht als Parameter angegeben, gehört ein Unix-Socket erstmal der gleichen Gruppe */
    if (opt_clientgroup == NULL)
        client_gid = gid;

    /* wenn inet in optarg gefunden wird *und* das auch noch direkt am Anfang
     * dann ist's kein lokaler socket */
    if (((p = strstr(opt_miltersocket, "inet")) != NULL) && opt_miltersocket == p)
        localsocket = 0;

    if (localsocket == 1) {
        /* den Socket oeffnen */
        if (smfi_opensocket(REMOVE_EXISTING_SOCKETS) != MI_SUCCESS) {
            logmsg(LOG_ERR, "could not open milter socket %s", opt_miltersocket);
            exit(EX_SOFTWARE);
        }
        /* testen, ob's den Socket nun gibt */
        p = opt_miltersocket + strlen("local:");
        if (stat(p, &st) < 0) {
            p = opt_miltersocket + strlen("unix:");
            if (stat(p, &st) < 0) {
                logmsg(LOG_ERR, "miltersocket does not exist: %m", strerror(errno));
                exit(EX_DATAERR);
            }
        }

        /* gid der Gruppe root */
        if ((gr = getgrnam("root")) == NULL) {
            logmsg(LOG_ERR, "unknown rootgroup: getgrnam(root) failed");
            exit(EX_SOFTWARE);
        }
        root_gid = gr->gr_gid;

        /* clientgroup muss != root und != opt_group sein */
        if ((client_gid == gid) || (client_gid == root_gid)) {
            logmsg(LOG_ERR, "clientgroup %s must be neither %s nor %s", opt_clientgroup, "root", opt_group);
            exit(EX_DATAERR);
        }

        /* nun die Rechte setzen */
        if (chown(p, uid, client_gid) != 0) {
            logmsg(LOG_ERR, "chown(%s, %i, %i) failed: %m", p, uid, client_gid, strerror(errno));
            exit(EX_SOFTWARE);
        }
        if (chmod(p, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) != 0) {
            logmsg(LOG_ERR, "chmod(%s, 0660) failed: %m", p, strerror(errno));
            exit(EX_SOFTWARE);
        }

        logmsg(LOG_INFO, "changed socket %s to owner/group: %i/%i, mode: 0660", opt_miltersocket, uid, client_gid);
    }

    /* gid/uid setzen */
    if (setgid(gid) != 0) {
        logmsg(LOG_ERR, "setgid(%i) failed: %s", gr->gr_gid, strerror(errno));
        exit(EX_SOFTWARE);
    }
    if (setuid(uid) != 0) {
        logmsg(LOG_ERR, "setuid(%i) failed: %s", pw->pw_uid, strerror(errno));
        exit(EX_SOFTWARE);
    } 

    /* aktuelle uid/gid pruefen und loggen */
    uid = getuid();
    gid = getgid();
    if (uid == 0 || gid == 0) {
        logmsg(LOG_ERR, "too much priveleges, %s will not start under root", STR_PROGNAME);
        exit(EX_DATAERR);
    }
    logmsg(LOG_INFO, "running as uid: %i, gid: %i", (int) uid, (int) gid);

    if (opt_keepdir != NULL) {
        if (S_IRWXO & st.st_mode) {
            logmsg(LOG_ERR, "directory to keep data: %s: permissions too open: remove any access for other", opt_keepdir);
            exit(EX_DATAERR);
        }
        if (access(opt_keepdir, R_OK) < 0 && errno == EACCES) {
            logmsg(LOG_ERR, "directory to keep data: %s: permissions too strong: no read access", opt_keepdir);
            exit(EX_DATAERR);
        }
        if (access(opt_keepdir, W_OK) < 0 && errno == EACCES) {
            logmsg(LOG_ERR, "directory to keep data: %s: permissions too strong: no write access", opt_keepdir);
            exit(EX_DATAERR);
        }
        if (access(opt_keepdir, X_OK) < 0 && errno == EACCES) {
            logmsg(LOG_ERR, "directory to keep data: %s: permissions too strong: no execute access", opt_keepdir);
            exit(EX_DATAERR);
        }
        logmsg(LOG_INFO, "directory to keep data: %s", opt_keepdir);
    }

    dict_open(opt_signingtable, &dict_signingtable);
    if (opt_modetable)
        dict_open(opt_modetable, &dict_modetable);

    /* initialize OpenSSL */
    SSL_library_init();
    OpenSSL_add_all_algorithms();

    /* get meaningful error messages */
    SSL_load_error_strings();
    ERR_load_crypto_strings();

    /* Statistik initialisieren */
    init_stats();

    /* Signal-Handler fuer SIGALRM */
    signal(SIGALRM, sig_handler);

    /* Run milter */
    if ((c = smfi_main()) != MI_SUCCESS)
        logmsg(LOG_ERR, "Milter startup failed");
    else
        logmsg(LOG_NOTICE, "stopping %s %s listening on %s", STR_PROGNAME, STR_PROGVERSION, opt_miltersocket);

    dict_close(&dict_signingtable);
    if (opt_modetable)
        dict_close(&dict_modetable);

    /* cleanup OpenSSL */
    ERR_free_strings();
    EVP_cleanup();

    output_stats();

#ifdef DMALLOC
    dmalloc_log_stats();
    dmalloc_log_unfreed();
    dmalloc_shutdown();
#endif
    exit(c);
}
Esempio n. 8
0
int
main(int argc, char **argv)
{
    int ch;
    mode_t omask;
    const char *oconn = OCONN;
    const char *user = USER;
    const char *group = "";
    sfsistat r = MI_FAILURE;
    const char *ofile = NULL;

    tzset();
    openlog("sa2scl-milter", LOG_PID | LOG_NDELAY, LOG_DAEMON);

    while ((ch = getopt(argc, argv, "dg:p:u:")) != -1) {
        switch (ch) {
        case 'd':
            debug = 1;
            break;
        case 'g':
            group = optarg;
            break;
        case 'p':
            oconn = optarg;
            break;
        case 'u':
            user = optarg;
            break;
        default:
            usage(argv[0]);
        }
    }
    if (argc != optind) {
        fprintf(stderr, "unknown command line argument: %s ...",
                argv[optind]);
        usage(argv[0]);
    }

    if (!strncmp(oconn, "unix:", 5))
        ofile = oconn + 5;
    else if (!strncmp(oconn, "local:", 6))
        ofile = oconn + 6;
    if (ofile != NULL)
        unlink(ofile);

    /* drop privileges */
    if (!getuid()) {
        struct passwd *pw;
        struct group *gr;

        if ((pw = getpwnam(user)) == NULL) {
            fprintf(stderr, "getpwnam: %s: %s\n", user,
                    strerror(errno));
            return (1);
        }
        if (strlen(group) == 0 || (gr = getgrnam(group)) == NULL) {
            /* use primary group of user */
            setgroups(1, &pw->pw_gid);
            if (setegid(pw->pw_gid) || setgid(pw->pw_gid)) {
                fprintf(stderr, "setgid: %s\n", strerror(errno));
                return (1);
            }
            omask = 0177;
        } else {
            /* custom group */
            setgroups(1, &gr->gr_gid);
            if (setegid(gr->gr_gid) || setgid(gr->gr_gid)) {
                fprintf(stderr, "setgid: %s\n", strerror(errno));
                return (1);
            }
            omask = 0117;
        }
        if (
#if ! ( __linux__ )
            seteuid(pw->pw_uid) ||
#endif
            setuid(pw->pw_uid)) {
            fprintf(stderr, "setuid: %s\n", strerror(errno));
            return (1);
        }
    }

    if (smfi_setconn((char *)oconn) != MI_SUCCESS) {
        fprintf(stderr, "smfi_setconn: %s: failed\n", oconn);
        goto done;
    }

    if (smfi_register(smfilter) != MI_SUCCESS) {
        fprintf(stderr, "smfi_register: failed\n");
        goto done;
    }

    /* daemonize (detach from controlling terminal) */
    if (!debug && daemon(0, 0)) {
        fprintf(stderr, "daemon: %s\n", strerror(errno));
        goto done;
    }
    umask(omask);

    msg(LOG_INFO, NULL, "smfi_main: started");
    r = smfi_main();
    if (r != MI_SUCCESS)
        msg(LOG_ERR, NULL, "smfi_main: terminating due to error");
    else
        msg(LOG_INFO, NULL, "smfi_main: terminating without error");

done:
    return (r);
}
Esempio n. 9
0
int main(int argc, char **argv) {
    char *my_socket, *pt;
    const struct optstruct *opt;
    struct optstruct *opts;
    time_t currtime;
    int ret;

    memset(&descr, 0, sizeof(struct smfiDesc));
    descr.xxfi_name = "ClamAV";			/* filter name */
    descr.xxfi_version = SMFI_VERSION;		/* milter version */
    descr.xxfi_flags = SMFIF_QUARANTINE;	/* flags */
    descr.xxfi_connect = clamfi_connect;	/* connection info filter */
    descr.xxfi_envfrom = clamfi_envfrom;	/* envelope sender filter */
    descr.xxfi_envrcpt = clamfi_envrcpt;	/* envelope recipient filter */
    descr.xxfi_header = clamfi_header;		/* header filter */
    descr.xxfi_body = clamfi_body;		/* body block */
    descr.xxfi_eom = clamfi_eom;		/* end of message */
    descr.xxfi_abort = clamfi_abort;		/* message aborted */

    opts = optparse(NULL, argc, argv, 1, OPT_MILTER, 0, NULL);
    if (!opts) {
	mprintf("!Can't parse command line options\n");
	return 1;
    }

    if(optget(opts, "help")->enabled) {
	printf("Usage: %s [-c <config-file>]\n\n", argv[0]);
	printf("    --help                   -h       Show this help\n");
	printf("    --version                -V       Show version and exit\n");
	printf("    --config-file <file>     -c       Read configuration from file\n\n");
	optfree(opts);
	return 0;
    }

    if(opts->filename) {
	int x;
	for(x = 0; opts->filename[x]; x++)
	    mprintf("^Ignoring option %s\n", opts->filename[x]);
    }

    if(optget(opts, "version")->enabled) {
	printf("clamav-milter %s\n", get_version());
	optfree(opts);
	return 0;
    }

    pt = strdup(optget(opts, "config-file")->strarg);
    if((opts = optparse(pt, 0, NULL, 1, OPT_MILTER, 0, opts)) == NULL) {
	printf("%s: cannot parse config file %s\n", argv[0], pt);
	free(pt);
	return 1;
    }
    free(pt);

    if((opt = optget(opts, "Chroot"))->enabled) {
	if(chdir(opt->strarg) != 0) {
	    logg("!Cannot change directory to %s\n", opt->strarg);
	    return 1;
	}
	if(chroot(opt->strarg) != 0) {
	    logg("!chroot to %s failed. Are you root?\n", opt->strarg);
	    return 1;
	}
    }

    if(geteuid() == 0 && (opt = optget(opts, "User"))->enabled) {
        struct passwd *user = NULL;
	if((user = getpwnam(opt->strarg)) == NULL) {
	    fprintf(stderr, "ERROR: Can't get information about user %s.\n", opt->strarg);
	    optfree(opts);
	    return 1;
	}

	if(optget(opts, "AllowSupplementaryGroups")->enabled) {
#ifdef HAVE_INITGROUPS
	    if(initgroups(opt->strarg, user->pw_gid)) {
		fprintf(stderr, "ERROR: initgroups() failed.\n");
		optfree(opts);
		return 1;
	    }
#else
	    mprintf("!AllowSupplementaryGroups: initgroups() is not available, please disable AllowSupplementaryGroups\n");
	    optfree(opts);
	    return 1;
#endif
	} else {
#ifdef HAVE_SETGROUPS
	    if(setgroups(1, &user->pw_gid)) {
		fprintf(stderr, "ERROR: setgroups() failed.\n");
		optfree(opts);
		return 1;
	    }
#endif
	}

	if(setgid(user->pw_gid)) {
	    fprintf(stderr, "ERROR: setgid(%d) failed.\n", (int) user->pw_gid);
	    optfree(opts);
	    return 1;
	}

	if(setuid(user->pw_uid)) {
	    fprintf(stderr, "ERROR: setuid(%d) failed.\n", (int) user->pw_uid);
	    optfree(opts);
	    return 1;
	}
    }

    logg_lock = !optget(opts, "LogFileUnlock")->enabled;
    logg_time = optget(opts, "LogTime")->enabled;
    logg_size = optget(opts, "LogFileMaxSize")->numarg;
    logg_verbose = mprintf_verbose = optget(opts, "LogVerbose")->enabled;

    if((opt = optget(opts, "LogFile"))->enabled) {
	logg_file = opt->strarg;
	if(strlen(logg_file) < 2 || logg_file[0] != '/') {
	    fprintf(stderr, "ERROR: LogFile requires full path.\n");
	    logg_close();
	    optfree(opts);
	    return 1;
	}
    } else
	logg_file = NULL;

#if defined(USE_SYSLOG) && !defined(C_AIX)
    if(optget(opts, "LogSyslog")->enabled) {
	int fac;

	opt = optget(opts, "LogFacility");
	if((fac = logg_facility(opt->strarg)) == -1) {
	    logg("!LogFacility: %s: No such facility.\n", opt->strarg);
	    logg_close();
	    optfree(opts);
	    return 1;
	}

	openlog("clamav-milter", LOG_PID, fac);
	logg_syslog = 1;
    }
#endif

    time(&currtime);
    if(logg("#+++ Started at %s", ctime(&currtime))) {
	fprintf(stderr, "ERROR: Can't initialize the internal logger\n");
	logg_close();
	optfree(opts);
	return 1;
    }
    if((opt = optget(opts, "TemporaryDirectory"))->enabled)
	tempdir = opt->strarg;

    if(localnets_init(opts) || init_actions(opts)) {
	logg_close();
	optfree(opts);
	return 1;
    }

    if((opt = optget(opts, "Whitelist"))->enabled && whitelist_init(opt->strarg)) {
	localnets_free();
	logg_close();
	optfree(opts);
	return 1;
    }

    if((opt = optget(opts, "SkipAuthenticated"))->enabled && smtpauth_init(opt->strarg)) {
	localnets_free();
	whitelist_free();
	logg_close();
	optfree(opts);
	return 1;
    }

    pt = optget(opts, "AddHeader")->strarg;
    if(strcasecmp(pt, "No")) {
	char myname[255];

	if(!gethostname(myname, sizeof(myname))) {
	    myname[sizeof(myname)-1] = '\0';
	    snprintf(xvirushdr, sizeof(xvirushdr), "clamav-milter %s at %s", get_version(), myname);
	    xvirushdr[sizeof(xvirushdr)-1] = '\0';
	} else {
	    snprintf(xvirushdr, sizeof(xvirushdr), "clamav-milter %s", get_version());
	    xvirushdr[sizeof(xvirushdr)-1] = '\0';
	}

	descr.xxfi_flags |= SMFIF_ADDHDRS;

	if(strcasecmp(pt, "Add")) { /* Replace or Yes */
	    descr.xxfi_flags |= SMFIF_CHGHDRS;
	    addxvirus = 1;
	} else { /* Add */
	    addxvirus = 2;
	}
    }
    
    if(!(my_socket = optget(opts, "MilterSocket")->strarg)) {
	logg("!Please configure the MilterSocket directive\n");
	localnets_free();
	whitelist_free();
	logg_close();
	optfree(opts);
	return 1;
    }

    if(!optget(opts, "Foreground")->enabled) {
	if(daemonize() == -1) {
	    logg("!daemonize() failed\n");
	    localnets_free();
	    whitelist_free();
	    cpool_free();
	    logg_close();
	    optfree(opts);
	    return 1;
	}
	if(chdir("/") == -1)
	    logg("^Can't change current working directory to root\n");
    }

    if(smfi_setconn(my_socket) == MI_FAILURE) {
	logg("!smfi_setconn failed\n");
	localnets_free();
	whitelist_free();
	logg_close();
	optfree(opts);
	return 1;
    }
    if(smfi_register(descr) == MI_FAILURE) {
	logg("!smfi_register failed\n");
	localnets_free();
	whitelist_free();
	logg_close();
	optfree(opts);
	return 1;
    }
    opt = optget(opts, "FixStaleSocket");
    if(smfi_opensocket(opt->enabled) == MI_FAILURE) {
	logg("!Failed to create socket %s\n", my_socket);
	localnets_free();
	whitelist_free();
	logg_close();
	optfree(opts);
	return 1;
    }

    maxfilesize = optget(opts, "MaxFileSize")->numarg;
    readtimeout = optget(opts, "ReadTimeout")->numarg;

    cpool_init(opts);
    if (!cp) {
	logg("!Failed to init the socket pool\n");
	localnets_free();
	whitelist_free();
	logg_close();
	optfree(opts);
	return 1;
    }	

    if((opt = optget(opts, "PidFile"))->enabled) {
	FILE *fd;
	mode_t old_umask = umask(0006);

	if((fd = fopen(opt->strarg, "w")) == NULL) {
	    logg("!Can't save PID in file %s\n", opt->strarg);
	} else {
	    if (fprintf(fd, "%u", (unsigned int)getpid())<0) {
	    	logg("!Can't save PID in file %s\n", opt->strarg);
	    }
	    fclose(fd);
	}
	umask(old_umask);
    }

    ret = smfi_main();

    optfree(opts);

    logg_close();
    cpool_free();
    localnets_free();
    whitelist_free();

    return ret;
}
Esempio n. 10
0
int     main(int argc, char **argv)
{
    char   *action = 0;
    char   *command = 0;
    const struct command_map *cp;
    int     ch;
    int     code;
    const char **cpp;
    char   *set_macro_state_arg = 0;
    char   *nosend = 0;
    char   *noreply = 0;
    const struct noproto_map *np;

    while ((ch = getopt(argc, argv, "a:A:b:c:C:d:f:h:i:lm:M:n:N:p:rv")) > 0) {
	switch (ch) {
	case 'a':
	    action = optarg;
	    break;
	case 'A':
	    if (rcpt_count >= MAX_RCPT) {
		fprintf(stderr, "too many -A options\n");
		exit(1);
	    }
	    rcpt_addr[rcpt_count++] = optarg;
	    break;
	case 'b':
#ifdef SMFIR_REPLBODY
	    if (body_file) {
		fprintf(stderr, "too many -b options\n");
		exit(1);
	    }
	    body_file = optarg;
#else
	    fprintf(stderr, "no libmilter support to replace body\n");
#endif
	    break;
	case 'c':
	    command = optarg;
	    break;
	case 'd':
	    if (smfi_setdbg(atoi(optarg)) == MI_FAILURE) {
		fprintf(stderr, "smfi_setdbg failed\n");
		exit(1);
	    }
	    break;
	case 'f':
#ifdef SMFIR_CHGFROM
	    if (chg_from) {
		fprintf(stderr, "too many -f options\n");
		exit(1);
	    }
	    chg_from = optarg;
#else
	    fprintf(stderr, "no libmilter support to change sender\n");
	    exit(1);
#endif
	    break;
	case 'h':
#ifdef SMFIR_CHGHEADER
	    if (chg_hdr) {
		fprintf(stderr, "too many -h options\n");
		exit(1);
	    }
	    parse_hdr_info(optarg, &chg_idx, &chg_hdr, &chg_val);
#else
	    fprintf(stderr, "no libmilter support to change header\n");
	    exit(1);
#endif
	    break;
	case 'i':
#ifdef SMFIR_INSHEADER
	    if (ins_hdr) {
		fprintf(stderr, "too many -i options\n");
		exit(1);
	    }
	    parse_hdr_info(optarg, &ins_idx, &ins_hdr, &ins_val);
#else
	    fprintf(stderr, "no libmilter support to insert header\n");
	    exit(1);
#endif
	    break;
	case 'l':
#if SMFI_VERSION > 5
	    if (ins_hdr || chg_hdr) {
		fprintf(stderr, "specify -l before -i or -r\n");
		exit(1);
	    }
	    misc_mask |= SMFIP_HDR_LEADSPC;
#else
	    fprintf(stderr, "no libmilter support for leading space\n");
	    exit(1);
#endif
	    break;
	case 'm':
#if SMFI_VERSION > 5
	    if (set_macro_state_arg) {
		fprintf(stderr, "too many -m options\n");
		exit(1);
	    }
	    set_macro_state_arg = optarg;
#else
	    fprintf(stderr, "no libmilter support to specify macro list\n");
	    exit(1);
#endif
	    break;
	case 'M':
#if SMFI_VERSION > 5
	    if (set_macro_list) {
		fprintf(stderr, "too many -M options\n");
		exit(1);
	    }
	    set_macro_list = optarg;
#else
	    fprintf(stderr, "no libmilter support to specify macro list\n");
#endif
	    break;
	case 'n':
#if SMFI_VERSION > 5
	    if (nosend) {
		fprintf(stderr, "too many -n options\n");
		exit(1);
	    }
	    nosend = optarg;
#else
	    fprintf(stderr, "no libmilter support for negotiate callback\n");
#endif
	    break;
	case 'N':
#if SMFI_VERSION > 5
	    if (noreply) {
		fprintf(stderr, "too many -n options\n");
		exit(1);
	    }
	    noreply = optarg;
#else
	    fprintf(stderr, "no libmilter support for negotiate callback\n");
#endif
	    break;
	case 'p':
	    if (smfi_setconn(optarg) == MI_FAILURE) {
		fprintf(stderr, "smfi_setconn failed\n");
		exit(1);
	    }
	    break;
	case 'r':
#ifdef SMFIP_RCPT_REJ
	    misc_mask |= SMFIP_RCPT_REJ;
#else
	    fprintf(stderr, "no libmilter support for rejected recipients\n");
#endif
	    break;
	case 'v':
	    verbose++;
	    break;
	case 'C':
	    conn_count = atoi(optarg);
	    break;
	default:
	    fprintf(stderr,
		    "usage: %s [-dv] \n"
		    "\t[-a action]              non-default action\n"
		    "\t[-b body_text]           replace body\n",
		    "\t[-c command]             non-default action trigger\n"
		    "\t[-h 'index label value'] replace header\n"
		    "\t[-i 'index label value'] insert header\n"
		    "\t[-m macro_state]		non-default macro state\n"
		    "\t[-M macro_list]		non-default macro list\n"
		    "\t[-n events]		don't receive these events\n"
		  "\t[-N events]		don't reply to these events\n"
		    "\t-p port                  milter application\n"
		  "\t-r                       request rejected recipients\n"
		    "\t[-C conn_count]          when to exit\n",
		    argv[0]);
	    exit(1);
	}
    }
    if (command) {
	for (cp = command_map; /* see below */ ; cp++) {
	    if (cp->name == 0) {
		fprintf(stderr, "bad -c argument: %s\n", command);
		exit(1);
	    }
	    if (strcmp(command, cp->name) == 0)
		break;
	}
    }
    if (action) {
	if (command == 0)
	    cp = command_map;
	if (strcmp(action, "tempfail") == 0) {
	    cp->reply[0] = SMFIS_TEMPFAIL;
	} else if (strcmp(action, "reject") == 0) {
	    cp->reply[0] = SMFIS_REJECT;
	} else if (strcmp(action, "accept") == 0) {
	    cp->reply[0] = SMFIS_ACCEPT;
	} else if (strcmp(action, "discard") == 0) {
	    cp->reply[0] = SMFIS_DISCARD;
#ifdef SMFIS_SKIP
	} else if (strcmp(action, "skip") == 0) {
	    cp->reply[0] = SMFIS_SKIP;
#endif
	} else if ((code = atoi(action)) >= 400
		   && code <= 599
		   && action[3] == ' ') {
	    cp->reply[0] = SMFIR_REPLYCODE;
	    reply_code = action;
	    reply_dsn = action + 3;
	    if (*reply_dsn != 0) {
		*reply_dsn++ = 0;
		reply_dsn += strspn(reply_dsn, " ");
	    }
	    if (*reply_dsn == 0) {
		reply_dsn = reply_message = 0;
	    } else {
		reply_message = reply_dsn + strcspn(reply_dsn, " ");
		if (*reply_message != 0) {
		    *reply_message++ = 0;
		    reply_message += strspn(reply_message, " ");
		}
		if (*reply_message == 0)
		    reply_message = 0;
	    }
	} else {
	    fprintf(stderr, "bad -a argument: %s\n", action);
	    exit(1);
	}
	if (verbose) {
	    printf("command %s action %d\n", cp->name, cp->reply[0]);
	    if (reply_code)
		printf("reply code %s dsn %s message %s\n",
		       reply_code, reply_dsn ? reply_dsn : "(null)",
		       reply_message ? reply_message : "(null)");
	}
    }
#if SMFI_VERSION > 5
    if (set_macro_state_arg) {
	for (cpp = macro_states; /* see below */ ; cpp++) {
	    if (*cpp == 0) {
		fprintf(stderr, "bad -m argument: %s\n", set_macro_state_arg);
		exit(1);
	    }
	    if (strcmp(set_macro_state_arg, *cpp) == 0)
		break;
	}
	set_macro_state = cpp - macro_states;
    }
    if (nosend) {
	for (np = noproto_map; /* see below */ ; np++) {
	    if (np->name == 0) {
		fprintf(stderr, "bad -n argument: %s\n", nosend);
		exit(1);
	    }
	    if (strcmp(nosend, np->name) == 0)
		break;
	}
	nosend_mask = np->send_mask;
	np->action[0] = 0;
    }
    if (noreply) {
	for (np = noproto_map; /* see below */ ; np++) {
	    if (np->name == 0) {
		fprintf(stderr, "bad -N argument: %s\n", noreply);
		exit(1);
	    }
	    if (strcmp(noreply, np->name) == 0)
		break;
	}
	noreply_mask = np->reply_mask;
	*np->reply = SMFIS_NOREPLY;
    }
#endif
    if (smfi_register(smfilter) == MI_FAILURE) {
	fprintf(stderr, "smfi_register failed\n");
	exit(1);
    }
    return (smfi_main());
}
Esempio n. 11
0
int
main(int argc, char **argv)
{
	int ch;
	const char *oconn = OCONN;
	const char *user = USER;
	sfsistat r = MI_FAILURE;
	const char *ofile = NULL;

	tzset();
	openlog("milter-spamd", LOG_PID | LOG_NDELAY, LOG_DAEMON);

	while ((ch = getopt(argc, argv, "di:p:u:U:")) != -1) {
		switch (ch) {
		case 'd':
			debug = 1;
			break;
		case 'i':  {
			int r;

			ignore_connect = optarg;
			r = regcomp(&re_ignore_connect, ignore_connect,
			    REG_EXTENDED | REG_ICASE);
			if (r) {
				char e[8192];

				regerror(r, &re_ignore_connect, e, sizeof(e));
				fprintf(stderr, "regcomp: %s: %s\n",
				    ignore_connect, e);
				usage(argv[0]);
			}
			break;
		}
		case 'p':
			oconn = optarg;
			break;
		case 'u':
			user = optarg;
			break;
		case 'U':
			spamd_user = optarg;
			break;
		default:
			usage(argv[0]);
		}
	}
	if (argc != optind) {
		fprintf(stderr, "unknown command line argument: %s ...",
		    argv[optind]);
		usage(argv[0]);
	}

	if (!strncmp(oconn, "unix:", 5))
		ofile = oconn + 5;
	else if (!strncmp(oconn, "local:", 6))
		ofile = oconn + 6;
	if (ofile != NULL)
		unlink(ofile);

	/* drop privileges */
	if (!getuid()) {
		struct passwd *pw;

		if ((pw = getpwnam(user)) == NULL) {
			fprintf(stderr, "getpwnam: %s: %s\n", user,
			    strerror(errno));
			return (1);
		}
		setgroups(1, &pw->pw_gid);
		if (setegid(pw->pw_gid) || setgid(pw->pw_gid)) {
			fprintf(stderr, "setgid: %s\n", strerror(errno));
			return (1);
		}
		if (
		    seteuid(pw->pw_uid) ||
		    setuid(pw->pw_uid)) {
			fprintf(stderr, "setuid: %s\n", strerror(errno));
			return (1);
		}
	}

	if (smfi_setconn((char *)oconn) != MI_SUCCESS) {
		fprintf(stderr, "smfi_setconn: %s: failed\n", oconn);
		goto done;
	}

	if (smfi_register(smfilter) != MI_SUCCESS) {
		fprintf(stderr, "smfi_register: failed\n");
		goto done;
	}

	/* daemonize (detach from controlling terminal) */
	if (!debug && daemon(0, 0)) {
		fprintf(stderr, "daemon: %s\n", strerror(errno));
		goto done;
	}
	umask(0177);
	signal(SIGPIPE, SIG_IGN);

	msg(LOG_INFO, NULL, "started: %s", rcsid);
	r = smfi_main();
	if (r != MI_SUCCESS)
		msg(LOG_ERR, NULL, "smfi_main: terminating due to error");
	else
		msg(LOG_INFO, NULL, "smfi_main: terminating without error");

done:
	return (r);
}
Esempio n. 12
0
/* {{{ main
*/
int main(int argc, char *argv[])
{
    char *sock = NULL;
	int dofork = 0;

	int exit_status = SUCCESS;
	int c;
/* temporary locals */
	int orig_optind=ap_php_optind;
	char *orig_optarg=ap_php_optarg;
	int interactive=0;
	char *param_error=NULL;
/* end of temporary locals */

	void ***tsrm_ls;

#ifdef HAVE_SIGNAL_H
#if defined(SIGPIPE) && defined(SIG_IGN)
	signal(SIGPIPE, SIG_IGN); /* ignore SIGPIPE in standalone mode so
								that sockets created via fsockopen()
								don't kill PHP if the remote site
								closes it.  in apache|apxs mode apache
								does that for us!  [email protected]
								20000419 */
#endif
#endif


	tsrm_startup(1, 1, 0, NULL);
	tsrm_ls = ts_resource(0);
	sapi_startup(&milter_sapi_module);
	
	while ((c=ap_php_getopt(argc, argv, OPTSTRING))!=-1) {
		switch (c) {
		case 'c':
			milter_sapi_module.php_ini_path_override = strdup(ap_php_optarg);
			break;
		case 'n':
			milter_sapi_module.php_ini_ignore = 1;
			break;
		}
	}
	ap_php_optind = orig_optind;
	ap_php_optarg = orig_optarg;

	milter_sapi_module.executable_location = argv[0];


	sapi_module.startup(&milter_sapi_module);

	zend_first_try {
		while ((c=ap_php_getopt(argc, argv, OPTSTRING))!=-1) {
			switch (c) {
			case '?':
				php_output_tearup();
				SG(headers_sent) = 1;
				php_milter_usage(argv[0]);
				php_output_teardown();
				exit(1);
				break;
			}
		}
		ap_php_optind = orig_optind;
		ap_php_optarg = orig_optarg;

        /* Set some CLI defaults */
		SG(options) |= SAPI_OPTION_NO_CHDIR;
		zend_alter_ini_entry("html_errors", 12, "0", 1, PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
		zend_alter_ini_entry("max_execution_time", 19, "0", 1, PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);

		zend_uv.html_errors = 0; /* tell the engine we're in non-html mode */

		while ((c = ap_php_getopt(argc, argv, OPTSTRING)) != -1) {
			switch (c) {

			case 'a':	/* interactive mode */
				printf("Interactive mode enabled\n\n");
				interactive=1;
				break;

			case 'C': /* don't chdir to the script directory */
				/* This is default so NOP */
				break;
			case 'd': /* define ini entries on command line */
				define_command_line_ini_entry(ap_php_optarg);
				break;

			case 'D': /* daemon */
				dofork = 1;
				break;

			case 'e': /* enable extended info output */
				CG(compiler_options) |= ZEND_COMPILE_EXTENDED_INFO;
				break;

			case 'f': /* parse file */
				filename = ap_php_optarg;
				break;

			case 'h': /* help & quit */
			case '?':
				php_output_tearup();
				SG(headers_sent) = 1;
				php_milter_usage(argv[0]);
				php_output_teardown();
				exit(1);
				break;

			case 'p': /* socket */
				sock = strdup(ap_php_optarg);
				break;

			case 'v': /* show php version & quit */
				if (php_request_startup()==FAILURE) {
					zend_ini_deactivate();
					php_module_shutdown();
					sapi_shutdown();
					tsrm_shutdown();

					exit(1);
				}
				SG(headers_sent) = 1;
				SG(request_info).no_headers = 1;
				php_printf("PHP %s (%s) (built: %s %s)\nCopyright (c) 1997-2014 The PHP Group\n%s", PHP_VERSION, sapi_module.name, __DATE__, __TIME__, get_zend_version());
				php_output_teardown();
				exit(1);
				break;

			case 'V': /* verbose */
				flag_debug = atoi(ap_php_optarg);
				break;

			case 'z': /* load extension file */
				zend_load_extension(ap_php_optarg);
				break;

			default:
				break;
			}
		}

		if (param_error) {
			SG(headers_sent) = 1;
			SG(request_info).no_headers = 1;
			PUTS(param_error);
			exit(1);
		}

		/* only set script_file if not set already and not in direct mode and not at end of parameter list */
		if (argc > ap_php_optind && !filename) {
			filename=argv[ap_php_optind];
			ap_php_optind++;
		}

		/* check if file exists, exit else */
		
		if (dofork) {
			switch(fork()) {
				case -1: /* Uh-oh, we have a problem forking. */
					fprintf(stderr, "Uh-oh, couldn't fork!\n");
					exit(errno);
					break;
				case 0: /* Child */
					break;
				default: /* Parent */
					exit(0);
			}
		}
			
		if (sock) {
			struct stat junk;
			if (stat(sock,&junk) == 0) unlink(sock);
		}

		openlog("php-milter", LOG_PID, LOG_MAIL);
		
		if ((exit_status = mlfi_init())) {
			syslog(1, "mlfi_init failed.");
			closelog();
			goto err;
		}

		smfi_setconn(sock);
		if (smfi_register(smfilter) == MI_FAILURE) {
			syslog(1, "smfi_register failed.");
			fprintf(stderr, "smfi_register failed\n");
			closelog();
			goto err;
		} else {
			exit_status = smfi_main();
		}			

		closelog();

		if (milter_sapi_module.php_ini_path_override) {
			free(milter_sapi_module.php_ini_path_override);
		}

	} zend_catch {
		exit_status = EG(exit_status);
	} zend_end_try();

err:
	php_module_shutdown();
	sapi_shutdown();
	tsrm_shutdown();

	exit(exit_status);
}
Esempio n. 13
0
int main(int argc, char **argv)
{
	const char *opts = "b:D:dg:hst:u:H";
#ifdef HAS_LONGOPT
	static const struct option lopt[] = {
		{"bind", 1, 0, 'b'},
		{"dry", 0, 0, 'H'},
		{"debug", 1, 0, 'D'},
		{"daemonize", 0, 0, 'd'},
		{"group", 1, 0, 'g'},
		{"help", 0, 0, 'h'},
		{"no-stamp", 0, 0, 's'},
		{"timeout", 1, 0, 't'},
		{"user", 1, 0, 'u'},
		{NULL, 0, 0, 0}
	};
#endif
	int c;
	char *p;
	char *oconn;
	int setconn;
	size_t len;
	int ret;
	uint8_t daemon;
	char *usr;
	char *grp;
	char *pidf = "/var/run/milter/dnsbl-milter.pid";

	config.pname = argv[0];
	p = strrchr(config.pname, '/');
	if (p != NULL)
		config.pname = p + 1;

	if (argc < 2) {
		usage(config.pname);
		exit(EX_USAGE);
	}

	setconn = 0;
	oconn = NULL;
	config.daemon = 0;
	daemon = 0;
	usr = grp = NULL;
	config.stamp = 1;

	while ((c = getopt_long(argc, argv, opts, lopt, NULL)) != -1) {

		switch (c) {

		case 'b':	/* bind address/socket */
			if (setconn != 0) {
				mlog(LOG_ERR,
				     "Bind address/socket already provided, ignoring");
				break;
			}

			if ((optarg == NULL) || (*optarg == '\0')) {
				mlog(LOG_ERR,
				     "No bind address/socket provided");
				usage(config.pname);
				exit(EX_USAGE);
			}

			if ((strncmp(optarg, "unix:", 5) == 0) ||
			    (strncmp(optarg, "local:", 6) == 0) ||
			    (strncmp(optarg, "inet:", 5) == 0) ||
			    (strncmp(optarg, "inet6:", 6) == 0)) {
				oconn = optarg;
				setconn = 1;
				break;
			}

			/* "unix:" + optarg + '\0' */
			len = 5 + strlen(optarg) + 1;
			oconn = malloc(len);
			if (oconn == NULL) {
				mlog(LOG_ERR, "Memory allocation failed");
				exit(EX_UNAVAILABLE);
			}

			snprintf(oconn, len, "unix:%s", optarg);
			setconn = 2;
			break;

		case 'H':
			config.drymode = 1;	// Adds a header instead of rejecting
			break;

		case 'D':
			if ((optarg == NULL) || (*optarg == '\0')) {
				mlog(LOG_ERR,
				     "No debugging level provided");
				usage(config.pname);
				exit(EX_USAGE);
			}

			smfi_setdbg(atoi(optarg));
			break;

		case 'd':
			daemon = 1;
			break;

		case 'g':
			if ((optarg == NULL) || (*optarg == '\0')) {
				mlog(LOG_ERR, "No group provided");
				usage(config.pname);
				exit(EX_USAGE);
			}

			grp = optarg;
			break;

		case 's':
			config.stamp = 0;
			break;

		case 't':
			if ((optarg == NULL) || (*optarg == '\0')) {
				mlog(LOG_ERR, "No timeout provided");
				usage(config.pname);
				exit(EX_USAGE);
			}

			smfi_settimeout(atoi(optarg));
			break;

		case 'u':
			if ((optarg == NULL) || (*optarg == '\0')) {
				mlog(LOG_ERR, "No user provided");
				usage(config.pname);
				exit(EX_USAGE);
			}

			usr = optarg;
			break;

		case 'h':	/* help */
		default:
			usage(config.pname);
			exit(EX_USAGE);
		}
	}

	if (setconn == 0) {
		mlog(LOG_ERR, "%s: Missing required bind address/socket\n",
		     config.pname);
		usage(config.pname);
		exit(EX_USAGE);
	}

	umask(0137);

	if ((oconn == NULL) || (smfi_setconn(oconn) == MI_FAILURE)) {
		mlog(LOG_ERR, "smfi_setconn() failed");
		exit(EX_UNAVAILABLE);
	}

	if (smfi_register(smfilter) == MI_FAILURE) {
		mlog(LOG_ERR, "smfi_register() failed");
		exit(EX_UNAVAILABLE);
	}

	/* List of blacklists to use */
	list_add(&blacklist, "bl.spamcop.net",
		 "Listed on SpamCop. See http://spamcop.net/w3m?action=checkblock&ip=");
	list_add(&blacklist, "b.barracudacentral.org",
		 "Listed on Barracuda Reputation Block List (BRBL). See http://www.barracudacentral.org/lookups?ip_address=");
	list_add(&blacklist, "zen.spamhaus.org",
		 "Listed on The Spamhaus Project. See http://www.spamhaus.org/query/bl?ip=");
	list_add(&blacklist, "psbl.surriel.com",
		 "Listed on The Passive Spam Block List. See http://psbl.surriel.com/listing?ip=");

	/* List of whitelists to use */
	list_add(&whitelist, "list.dnswl.org", "http://www.dnswl.org");

	if ((usr != NULL) || (grp != NULL))
		if (drop_privs(usr, grp) != 0)
			exit(EX_TEMPFAIL);

	if (daemon != 0)
		daemonize();

	/* write pid file */
	pidf_create(pidf);

	mlog(LOG_INFO, "Starting Sendmail %s filter '%s'",
	     smfilter.xxfi_name, config.pname);
	ret = smfi_main();
	/* remove pid file */
	pidf_destroy(pidf);
	if (ret == MI_SUCCESS) {
		mlog(LOG_INFO, "Stopping Sendmail %s filter '%s'",
		     smfilter.xxfi_name, config.pname);
	} else {
		mlog(LOG_ERR,
		     "Abnormal termination of Sendmail %s filter '%s': %d",
		     smfilter.xxfi_name, config.pname, ret);
	}

	list_free(&blacklist);
	list_free(&whitelist);

	if (setconn == 2) {
		free(oconn);
		oconn = NULL;
	}

	if (daemon != 0)
		closelog();

	return ret;
}
Esempio n. 14
0
int
main(int argc, char **argv)
{
	int u_flag;
	int s_flag;
	int d_flag;
	int W_flag;
	int w_flag;
	int l_flag;
	int ret;
	char *user;
	char *socket;
	char *ep;
	int uid;
	struct passwd *pwd;
	pid_t pid;
	int fd;

	u_flag = s_flag = d_flag = l_flag = w_flag = W_flag = 0;
	dnsbls = dnswls = wl_domains = NULL;

	while ((ret = getopt(argc, argv, "hdu:s:l:w:W:")) != -1) {
		switch(ret) {
		case 'd':
			d_flag++;
			break;
		case 'u':
			u_flag++;
			user = optarg;
			break;
		case 's':
			s_flag++;
			socket = optarg;
			break;
		case 'l':
			dnsbls = add_list(dnsbls, optarg);
			l_flag++;
			break;
		case 'w':
			dnswls = add_list(dnswls, optarg);
			w_flag++;
			break;
		case 'W':
			wl_domains = add_list(wl_domains, optarg);
			W_flag++;
			break;
		case 'h':
		default:
			usage(_progname);
		}
	}
	argc -= optind;
	argv += optind;

	if (!u_flag) {
		fprintf(stderr, "You must specify a user!\n");
		usage(_progname);
	}

	if (!s_flag) {
		fprintf(stderr, "You must specify a socket!\n");
		usage(_progname);
	}

	if (w_flag) {
		printf("Using whitelists:\n");
		print_list(dnswls);
	}

	if (W_flag) {
		printf("Whitelisting domains:\n");
		print_list(wl_domains);
	}

	if (!l_flag) {
		fprintf(stderr, "You must specify at least one DNSBL!\n");
		usage(_progname);
	} else {
		printf("Using blacklists:\n");
		print_list(dnsbls);
	}

	if (getuid() != 0) {
		fprintf(stderr, "%s: cannot switch to user %s if not started as root!\n", _progname, user);
		exit(EX_USAGE);
	}

	/* OK running as root, now switch to user */
	uid = strtol(user, &ep, 0);
	if (*ep == '\0') {
		pwd = getpwuid(uid);
	} else {
		pwd = getpwnam(user);
	}

	if (pwd == NULL) {
		fprintf(stderr, "%s: unknown user: %s\n", _progname, user);
		exit(EX_NOUSER);
	}

	if (setgroups(0, NULL) < 0) {
		perror("setgroups()");
		exit(1);
	}

	if (setgid(pwd->pw_gid) < 0) {
		perror("setgid()");
		exit(1);
	}

	if (initgroups(user, pwd->pw_gid) < 0) {
		perror("initgroups()"); /* not a critical failure */
	}

	if (setuid(pwd->pw_uid) < 0) {
		perror("setuid()");
		exit(1);
	}

	openlog(_progname, 0, LOG_MAIL);

	if (smfi_register(smfilter) == MI_FAILURE) {
		fprintf(stderr, "smfi_register() failed.\n");
		exit(EX_UNAVAILABLE);
	}

	if (smfi_setconn(socket) == MI_FAILURE) {
		fprintf(stderr, "smfi_setconn() %s\n", strerror(errno));
		exit(EX_SOFTWARE);
	}

	if (smfi_opensocket(1) == MI_FAILURE) {
		fprintf(stderr, "smfi_opensocket() %s\n", strerror(errno));
		exit(EX_SOFTWARE);
	}

	(void) smfi_settimeout(7200);

	if (!d_flag) {
		fprintf(stderr, "Warning: not starting as daemon");
	} else {
		if (daemon(0, 0) < 0) {
			perror("daemon()");
			exit(1);
		}
	}

	syslog(LOG_INFO, "%s started successfuly!", _progname);
	return(smfi_main()); /* start doing business */
}