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; }
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); }
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 */ }