NORETURN event_server_main(int argc, char **argv, MULTI_SERVER_FN service,...) { const char *myname = "event_server_main"; VSTREAM *stream = 0; char *root_dir = 0; char *user_name = 0; int debug_me = 0; int daemon_mode = 1; char *service_name = basename(argv[0]); int delay; int c; int fd; va_list ap; MAIL_SERVER_INIT_FN pre_init = 0; MAIL_SERVER_INIT_FN post_init = 0; MAIL_SERVER_LOOP_FN loop = 0; int key; char *transport = 0; #if 0 char *lock_path; VSTRING *why; #endif int alone = 0; int zerolimit = 0; WATCHDOG *watchdog; char *oname_val; char *oname; char *oval; const char *err; char *generation; int msg_vstream_needed = 0; int redo_syslog_init = 0; /* * Process environment options as early as we can. */ if (getenv(CONF_ENV_VERB)) msg_verbose = 1; if (getenv(CONF_ENV_DEBUG)) debug_me = 1; /* * Don't die when a process goes away unexpectedly. */ signal(SIGPIPE, SIG_IGN); /* * Don't die for frivolous reasons. */ #ifdef SIGXFSZ signal(SIGXFSZ, SIG_IGN); #endif /* * May need this every now and then. */ var_procname = mystrdup(basename(argv[0])); set_mail_conf_str(VAR_PROCNAME, var_procname); /* * Initialize logging and exit handler. Do the syslog first, so that its * initialization completes before we enter the optional chroot jail. */ msg_syslog_init(mail_task(var_procname), LOG_PID, LOG_FACILITY); if (msg_verbose) msg_info("daemon started"); /* * Check the Postfix library version as soon as we enable logging. */ MAIL_VERSION_CHECK; /* * Initialize from the configuration file. Allow command-line options to * override compiled-in defaults or configured parameter values. */ mail_conf_suck(); /* * Register dictionaries that use higher-level interfaces and protocols. */ mail_dict_init(); /* * After database open error, continue execution with reduced * functionality. */ dict_allow_surrogate = 1; /* * Pick up policy settings from master process. Shut up error messages to * stderr, because no-one is going to see them. */ opterr = 0; while ((c = GETOPT(argc, argv, "cdDi:lm:n:o:s:St:uvVz")) > 0) { switch (c) { case 'c': root_dir = "setme"; break; case 'd': daemon_mode = 0; break; case 'D': debug_me = 1; break; case 'i': mail_conf_update(VAR_MAX_IDLE, optarg); break; case 'l': alone = 1; break; case 'm': mail_conf_update(VAR_MAX_USE, optarg); break; case 'n': service_name = optarg; break; case 'o': oname_val = mystrdup(optarg); if ((err = split_nameval(oname_val, &oname, &oval)) != 0) msg_fatal("invalid \"-o %s\" option value: %s", optarg, err); mail_conf_update(oname, oval); if (strcmp(oname, VAR_SYSLOG_NAME) == 0) redo_syslog_init = 1; myfree(oname_val); break; case 's': if ((socket_count = atoi(optarg)) <= 0) msg_fatal("invalid socket_count: %s", optarg); break; case 'S': stream = VSTREAM_IN; break; case 'u': user_name = "setme"; break; case 't': transport = optarg; break; case 'v': msg_verbose++; break; case 'V': if (++msg_vstream_needed == 1) msg_vstream_init(mail_task(var_procname), VSTREAM_ERR); break; case 'z': zerolimit = 1; break; default: msg_fatal("invalid option: %c", c); break; } } /* * Initialize generic parameters. */ mail_params_init(); if (redo_syslog_init) msg_syslog_init(mail_task(var_procname), LOG_PID, LOG_FACILITY); /* * If not connected to stdin, stdin must not be a terminal. */ if (daemon_mode && stream == 0 && isatty(STDIN_FILENO)) { msg_vstream_init(var_procname, VSTREAM_ERR); msg_fatal("do not run this command by hand"); } /* * Application-specific initialization. */ va_start(ap, service); while ((key = va_arg(ap, int)) != 0) { switch (key) { case MAIL_SERVER_INT_TABLE: get_mail_conf_int_table(va_arg(ap, CONFIG_INT_TABLE *)); break; case MAIL_SERVER_LONG_TABLE: get_mail_conf_long_table(va_arg(ap, CONFIG_LONG_TABLE *)); break; case MAIL_SERVER_STR_TABLE: get_mail_conf_str_table(va_arg(ap, CONFIG_STR_TABLE *)); break; case MAIL_SERVER_BOOL_TABLE: get_mail_conf_bool_table(va_arg(ap, CONFIG_BOOL_TABLE *)); break; case MAIL_SERVER_TIME_TABLE: get_mail_conf_time_table(va_arg(ap, CONFIG_TIME_TABLE *)); break; case MAIL_SERVER_RAW_TABLE: get_mail_conf_raw_table(va_arg(ap, CONFIG_RAW_TABLE *)); break; case MAIL_SERVER_NINT_TABLE: get_mail_conf_nint_table(va_arg(ap, CONFIG_NINT_TABLE *)); break; case MAIL_SERVER_NBOOL_TABLE: get_mail_conf_nbool_table(va_arg(ap, CONFIG_NBOOL_TABLE *)); break; case MAIL_SERVER_PRE_INIT: pre_init = va_arg(ap, MAIL_SERVER_INIT_FN); break; case MAIL_SERVER_POST_INIT: post_init = va_arg(ap, MAIL_SERVER_INIT_FN); break; case MAIL_SERVER_LOOP: loop = va_arg(ap, MAIL_SERVER_LOOP_FN); break; case MAIL_SERVER_EXIT: event_server_onexit = va_arg(ap, MAIL_SERVER_EXIT_FN); break; case MAIL_SERVER_PRE_ACCEPT: event_server_pre_accept = va_arg(ap, MAIL_SERVER_ACCEPT_FN); break; case MAIL_SERVER_PRE_DISCONN: event_server_pre_disconn = va_arg(ap, MAIL_SERVER_DISCONN_FN); break; case MAIL_SERVER_IN_FLOW_DELAY: event_server_in_flow_delay = 1; break; case MAIL_SERVER_SOLITARY: if (stream == 0 && !alone) msg_fatal("service %s requires a process limit of 1", service_name); break; case MAIL_SERVER_UNLIMITED: if (stream == 0 && !zerolimit) msg_fatal("service %s requires a process limit of 0", service_name); break; case MAIL_SERVER_PRIVILEGED: if (user_name) msg_fatal("service %s requires privileged operation", service_name); break; case MAIL_SERVER_WATCHDOG: event_server_watchdog = *va_arg(ap, int *); break; case MAIL_SERVER_SLOW_EXIT: event_server_slow_exit = va_arg(ap, MAIL_SERVER_SLOW_EXIT_FN); break; default: msg_panic("%s: unknown argument type: %d", myname, key); } } va_end(ap); if (root_dir) root_dir = var_queue_dir; if (user_name) user_name = var_mail_owner; /* * Can options be required? */ if (stream == 0) { if (transport == 0) msg_fatal("no transport type specified"); if (strcasecmp(transport, MASTER_XPORT_NAME_INET) == 0) event_server_accept = event_server_accept_inet; else if (strcasecmp(transport, MASTER_XPORT_NAME_UNIX) == 0) event_server_accept = event_server_accept_local; #ifdef MASTER_XPORT_NAME_PASS else if (strcasecmp(transport, MASTER_XPORT_NAME_PASS) == 0) event_server_accept = event_server_accept_pass; #endif else msg_fatal("unsupported transport type: %s", transport); } /* * Retrieve process generation from environment. */ if ((generation = getenv(MASTER_GEN_NAME)) != 0) { if (!alldig(generation)) msg_fatal("bad generation: %s", generation); OCTAL_TO_UNSIGNED(event_server_generation, generation); if (msg_verbose) msg_info("process generation: %s (%o)", generation, event_server_generation); } /* * Optionally start the debugger on ourself. */ if (debug_me) debug_process(); /* * Traditionally, BSD select() can't handle multiple processes selecting * on the same socket, and wakes up every process in select(). See TCP/IP * Illustrated volume 2 page 532. We avoid select() collisions with an * external lock file. */ /* * XXX Can't compete for exclusive access to the listen socket because we * also have to monitor existing client connections for service requests. */ #if 0 if (stream == 0 && !alone) { lock_path = concatenate(DEF_PID_DIR, "/", transport, ".", service_name, (char *) 0); why = vstring_alloc(1); if ((event_server_lock = safe_open(lock_path, O_CREAT | O_RDWR, 0600, (struct stat *) 0, -1, -1, why)) == 0) msg_fatal("open lock file %s: %s", lock_path, vstring_str(why)); close_on_exec(vstream_fileno(event_server_lock), CLOSE_ON_EXEC); myfree(lock_path); vstring_free(why); } #endif /* * Set up call-back info. */ event_server_service = service; event_server_name = service_name; event_server_argv = argv + optind; /* * Run pre-jail initialization. */ if (chdir(var_queue_dir) < 0) msg_fatal("chdir(\"%s\"): %m", var_queue_dir); if (pre_init) pre_init(event_server_name, event_server_argv); /* * Optionally, restrict the damage that this process can do. */ resolve_local_init(); tzset(); chroot_uid(root_dir, user_name); /* * Run post-jail initialization. */ if (post_init) post_init(event_server_name, event_server_argv); /* * Are we running as a one-shot server with the client connection on * standard input? If so, make sure the output is written to stdout so as * to satisfy common expectation. */ if (stream != 0) { vstream_control(stream, VSTREAM_CTL_DOUBLE, VSTREAM_CTL_WRITE_FD, STDOUT_FILENO, VSTREAM_CTL_END); service(stream, event_server_name, event_server_argv); vstream_fflush(stream); event_server_exit(); } /* * Running as a semi-resident server. Service connection requests. * Terminate when we have serviced a sufficient number of clients, when * no-one has been talking to us for a configurable amount of time, or * when the master process terminated abnormally. */ if (var_idle_limit > 0) event_request_timer(event_server_timeout, (char *) 0, var_idle_limit); for (fd = MASTER_LISTEN_FD; fd < MASTER_LISTEN_FD + socket_count; fd++) { event_enable_read(fd, event_server_accept, CAST_INT_TO_CHAR_PTR(fd)); close_on_exec(fd, CLOSE_ON_EXEC); } event_enable_read(MASTER_STATUS_FD, event_server_abort, (char *) 0); close_on_exec(MASTER_STATUS_FD, CLOSE_ON_EXEC); close_on_exec(MASTER_FLOW_READ, CLOSE_ON_EXEC); close_on_exec(MASTER_FLOW_WRITE, CLOSE_ON_EXEC); watchdog = watchdog_create(event_server_watchdog, (WATCHDOG_FN) 0, (char *) 0); /* * The event loop, at last. */ while (var_use_limit == 0 || use_count < var_use_limit || client_count > 0) { if (event_server_lock != 0) { watchdog_stop(watchdog); if (myflock(vstream_fileno(event_server_lock), INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) < 0) msg_fatal("select lock: %m"); } watchdog_start(watchdog); delay = loop ? loop(event_server_name, event_server_argv) : -1; event_loop(delay); } event_server_exit(); }
int main(int argc, char **argv) { struct passwd *pw; struct group *gptr; char *arg, *cp; char buf[MAXPATHLEN]; int i, f, ch; struct stat stb; /* * Simulate setuid daemon w/ PRIV_END called. * We don't want lpr to actually be setuid daemon since that * requires that the lpr binary be owned by user daemon, which * is potentially unsafe. */ if ((pw = getpwnam(DEFUID)) == NULL) errx(1, "'lp' uid not in password file"); effective_uid = pw->pw_uid; real_uid = getuid(); effective_gid = pw->pw_gid; real_gid = getgid(); setresgid(real_gid, real_gid, effective_gid); setresuid(real_uid, real_uid, effective_uid); if (signal(SIGHUP, SIG_IGN) != SIG_IGN) signal(SIGHUP, cleanup); if (signal(SIGINT, SIG_IGN) != SIG_IGN) signal(SIGINT, cleanup); if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) signal(SIGQUIT, cleanup); if (signal(SIGTERM, SIG_IGN) != SIG_IGN) signal(SIGTERM, cleanup); gethostname(host, sizeof (host)); openlog("lpr", 0, LOG_LPR); while ((ch = getopt(argc, argv, ":#:1:2:3:4:C:J:P:T:U:cdfghi::klmnpqrstvw:")) != -1) { switch (ch) { case '#': /* n copies */ if (isdigit(*optarg)) { i = atoi(optarg); if (i > 0) ncopies = i; } case '4': /* troff fonts */ case '3': case '2': case '1': fonts[ch - '1'] = optarg; break; case 'C': /* classification spec */ hdr++; class = optarg; break; case 'J': /* job name */ hdr++; jobname = optarg; break; case 'P': /* specifiy printer name */ printer = optarg; break; case 'T': /* pr's title line */ title = optarg; break; case 'U': /* user name */ hdr++; person = optarg; break; case 'c': /* print cifplot output */ case 'd': /* print tex output (dvi files) */ case 'g': /* print graph(1G) output */ case 'l': /* literal output */ case 'n': /* print ditroff output */ case 'p': /* print using ``pr'' */ case 't': /* print troff output (cat files) */ case 'v': /* print vplot output */ format = ch; break; case 'f': /* print fortran output */ format = 'r'; break; case 'h': /* toggle want of header page */ hdr = !hdr; break; case 'i': /* indent output */ iflag++; if ( optarg == 0 || (indent = atoi(optarg)) < 0) indent = 8; break; case 'm': /* send mail when done */ mailflg++; break; case 'q': /* just q job */ qflag++; break; case 'r': /* remove file when done */ rflag++; break; case 's': /* try to link files */ sflag++; break; case 'k': /* try to relink files */ kflag++; rflag++; break; case 'w': /* versatec page width */ width = optarg; break; default: usage(); } } argc -= optind; argv += optind; if (printer == NULL && (printer = getenv("PRINTER")) == NULL) printer = DEFLP; chkprinter(printer); if (SC && ncopies > 1) errx(1, "multiple copies are not allowed"); if (MC > 0 && ncopies > MC) errx(1, "only %ld copies are allowed", MC); /* * Get the identity of the person doing the lpr using the same * algorithm as lprm. */ if (real_uid != DU || person == NULL) { if ((pw = getpwuid(real_uid)) == NULL) errx(1, "Who are you?"); if ((person = strdup(pw->pw_name)) == NULL) err(1, NULL); } /* * Check for restricted group access. */ if (RG != NULL && real_uid != DU) { if ((gptr = getgrnam(RG)) == NULL) errx(1, "Restricted group specified incorrectly"); if (gptr->gr_gid != getgid()) { while (*gptr->gr_mem != NULL) { if ((strcmp(person, *gptr->gr_mem)) == 0) break; gptr->gr_mem++; } if (*gptr->gr_mem == NULL) errx(1, "Not a member of the restricted group"); } } /* * Check to make sure queuing is enabled if real_uid is not root. */ (void)snprintf(buf, sizeof(buf), "%s/%s", SD, LO); if (real_uid && stat(buf, &stb) == 0 && (stb.st_mode & 010)) errx(1, "Printer queue is disabled"); /* * Initialize the control file. */ mktemps(); tfd = nfile(tfname); card('H', host); card('P', person); if (hdr && !SH) { if (jobname == NULL) { if (argc == 0) jobname = "stdin"; else jobname = (arg = strrchr(argv[0], '/')) ? arg + 1 : argv[0]; } card('J', jobname); card('C', class); card('L', person); } if (iflag) card('I', itoa(indent)); if (mailflg) card('M', person); if (format == 't' || format == 'n' || format == 'd') for (i = 0; i < 4; i++) if (fonts[i] != NULL) card('1'+i, fonts[i]); if (width != NULL) card('W', width); /* * Read the files and spool them. */ if (argc == 0) copy(0, " "); else while (argc--) { if (argv[0][0] == '-' && argv[0][1] == '\0') { /* use stdin */ copy(0, " "); argv++; continue; } if ((f = test(arg = *argv++)) < 0) continue; /* file unreasonable */ if (sflag && (cp = linked(arg)) != NULL) { (void)snprintf(buf, sizeof(buf), "%d %d", makedev( major(statb.st_dev), minor(statb.st_dev)), (int)statb.st_ino); card('S', buf); if (format == 'p') card('T', title ? title : arg); for (i = 0; i < ncopies; i++) card(format, &dfname[inchar-2]); card('U', &dfname[inchar-2]); if (f) card('U', cp); card('N', arg); dfname[inchar]++; nact++; continue; } if (kflag && rename(arg, dfname) == 0) { if (format == 'p') card('T', title ? title : arg); for (i = 0; i < ncopies; i++) card(format, &dfname[inchar-2]); card('U', &dfname[inchar-2]); card('N', arg); nact++; continue; } if (kflag || sflag) syslog(LOG_WARNING, "%s: not %slinked, copying instead", arg, kflag ? "re" : ""); if ((i = safe_open(arg, O_RDONLY, 0)) < 0) warn("%s", arg); else { copy(i, arg); (void)close(i); if (f && unlink(arg) < 0) warnx("%s: not removed", arg); } } if (nact) { (void)close(tfd); tfname[inchar]--; /* * Touch the control file to fix position in the queue. */ PRIV_START; if ((tfd = safe_open(tfname, O_RDWR|O_NOFOLLOW, 0)) >= 0) { char c; if (read(tfd, &c, 1) == 1 && lseek(tfd, (off_t)0, 0) == 0 && write(tfd, &c, 1) != 1) { warn("%s", tfname); tfname[inchar]++; cleanup(0); } (void)close(tfd); } #ifdef EMBED if (rename(tfname, cfname) < 0) { #else if (link(tfname, cfname) < 0) { #endif warn("cannot rename %s", cfname); tfname[inchar]++; cleanup(0); } #ifndef EMBED unlink(tfname); #endif PRIV_END; if (qflag) /* just q things up */ exit(0); if (!startdaemon(printer)) printf("jobs queued, but cannot start daemon.\n"); exit(0); } cleanup(0); return (1); /* NOTREACHED */ } /* * Create the file n and copy from file descriptor f. */ static void copy(int f, char *n) { int fd, i, nr, nc; char buf[BUFSIZ]; if (format == 'p') card('T', title ? title : n); for (i = 0; i < ncopies; i++) card(format, &dfname[inchar-2]); card('U', &dfname[inchar-2]); card('N', n); fd = nfile(dfname); nr = nc = 0; while ((i = read(f, buf, sizeof(buf))) > 0) { if (write(fd, buf, i) != i) { warn("%s", n); break; } nc += i; if (nc >= sizeof(buf)) { nc -= sizeof(buf); nr++; if (MX > 0 && nr > MX) { warnx("%s: copy file is too large", n); break; } } } (void)close(fd); if (nc==0 && nr==0) warnx("%s: empty input file", f ? n : "stdin"); else nact++; }
static void scanfile(const char *filename, struct cl_engine *engine, const struct optstruct *opts, unsigned int options) { int ret = 0, fd, included; unsigned i; const struct optstruct *opt; const char *virname; STATBUF sb; struct metachain chain; if((opt = optget(opts, "exclude"))->enabled) { while(opt) { if(match_regex(filename, opt->strarg) == 1) { if(!printinfected) logg("~%s: Excluded\n", filename); return; } opt = opt->nextarg; } } if((opt = optget(opts, "include"))->enabled) { included = 0; while(opt) { if(match_regex(filename, opt->strarg) == 1) { included = 1; break; } opt = opt->nextarg; } if(!included) { if(!printinfected) logg("~%s: Excluded\n", filename); return; } } /* argh, don't scan /proc files */ if(STAT(filename, &sb) != -1) { #ifdef C_LINUX if(procdev && sb.st_dev == procdev) { if(!printinfected) logg("~%s: Excluded (/proc)\n", filename); return; } #endif if(!sb.st_size) { if(!printinfected) logg("~%s: Empty file\n", filename); return; } info.rblocks += sb.st_size / CL_COUNT_PRECISION; } #ifndef _WIN32 if(geteuid()) if(checkaccess(filename, NULL, R_OK) != 1) { if(!printinfected) logg("~%s: Access denied\n", filename); info.errors++; return; } #endif memset(&chain, 0, sizeof(chain)); if(optget(opts, "archive-verbose")->enabled) { chain.chains = malloc(sizeof(*chain.chains)); if (chain.chains) { chain.chains[0] = strdup(filename); chain.n = 1; } } logg("*Scanning %s\n", filename); if((fd = safe_open(filename, O_RDONLY|O_BINARY)) == -1) { logg("^Can't open file %s: %s\n", filename, strerror(errno)); info.errors++; return; } if((ret = cl_scandesc_callback(fd, &virname, &info.blocks, engine, options, &chain)) == CL_VIRUS) { if(optget(opts, "archive-verbose")->enabled) { if (chain.n > 1) { char str[128]; int toolong = print_chain(&chain, str, sizeof(str)); logg("~%s%s!(%d)%s: %s FOUND\n", str, toolong ? "..." : "", chain.lastvir-1, chain.chains[chain.n-1], virname); } else if (chain.lastvir) logg("~%s!(%d): %s FOUND\n", filename, chain.lastvir-1, virname); } logg("~%s: %s FOUND\n", filename, virname); info.files++; info.ifiles++; if(bell) fprintf(stderr, "\007"); } else if(ret == CL_CLEAN) { if(!printinfected && printclean) mprintf("~%s: OK\n", filename); info.files++; } else { if(!printinfected) logg("~%s: %s ERROR\n", filename, cl_strerror(ret)); info.errors++; } for (i=0;i<chain.n;i++) free(chain.chains[i]); free(chain.chains); close(fd); if(ret == CL_VIRUS && action) action(filename); }
FILE * ns_os_openfile(const char *filename, mode_t mode, isc_boolean_t switch_user) { char strbuf[ISC_STRERRORSIZE], *f; FILE *fp; int fd; /* * Make the containing directory if it doesn't exist. */ f = strdup(filename); if (f == NULL) { isc__strerror(errno, strbuf, sizeof(strbuf)); ns_main_earlywarning("couldn't strdup() '%s': %s", filename, strbuf); return (NULL); } if (mkdirpath(f, ns_main_earlywarning) == -1) { free(f); return (NULL); } free(f); if (switch_user && runas_pw != NULL) { #ifndef HAVE_LINUXTHREADS gid_t oldgid = getgid(); #endif /* Set UID/GID to the one we'll be running with eventually */ setperms(runas_pw->pw_uid, runas_pw->pw_gid); fd = safe_open(filename, mode, ISC_FALSE); #ifndef HAVE_LINUXTHREADS /* Restore UID/GID to root */ setperms(0, oldgid); #endif /* HAVE_LINUXTHREADS */ if (fd == -1) { #ifndef HAVE_LINUXTHREADS fd = safe_open(filename, mode, ISC_FALSE); if (fd != -1) { ns_main_earlywarning("Required root " "permissions to open " "'%s'.", filename); } else { ns_main_earlywarning("Could not open " "'%s'.", filename); } ns_main_earlywarning("Please check file and " "directory permissions " "or reconfigure the filename."); #else /* HAVE_LINUXTHREADS */ ns_main_earlywarning("Could not open " "'%s'.", filename); ns_main_earlywarning("Please check file and " "directory permissions " "or reconfigure the filename."); #endif /* HAVE_LINUXTHREADS */ } } else { fd = safe_open(filename, mode, ISC_FALSE); } if (fd < 0) { isc__strerror(errno, strbuf, sizeof(strbuf)); ns_main_earlywarning("could not open file '%s': %s", filename, strbuf); return (NULL); } fp = fdopen(fd, "w"); if (fp == NULL) { isc__strerror(errno, strbuf, sizeof(strbuf)); ns_main_earlywarning("could not fdopen() file '%s': %s", filename, strbuf); } return (fp); }
void ns_os_writepidfile(const char *filename, isc_boolean_t first_time) { int fd; FILE *lockfile; size_t len; pid_t pid; char strbuf[ISC_STRERRORSIZE]; void (*report)(const char *, ...); /* * The caller must ensure any required synchronization. */ report = first_time ? ns_main_earlyfatal : ns_main_earlywarning; cleanup_pidfile(); if (strcmp(filename, "none") == 0) return; len = strlen(filename); pidfile = malloc(len + 1); if (pidfile == NULL) { isc__strerror(errno, strbuf, sizeof(strbuf)); (*report)("couldn't malloc '%s': %s", filename, strbuf); return; } /* This is safe. */ strcpy(pidfile, filename); fd = safe_open(filename, ISC_FALSE); if (fd < 0) { isc__strerror(errno, strbuf, sizeof(strbuf)); (*report)("couldn't open pid file '%s': %s", filename, strbuf); free(pidfile); pidfile = NULL; return; } lockfile = fdopen(fd, "w"); if (lockfile == NULL) { isc__strerror(errno, strbuf, sizeof(strbuf)); (*report)("could not fdopen() pid file '%s': %s", filename, strbuf); (void)close(fd); cleanup_pidfile(); return; } pid = getpid(); if (fprintf(lockfile, "%ld\n", (long)pid) < 0) { (*report)("fprintf() to pid file '%s' failed", filename); (void)fclose(lockfile); cleanup_pidfile(); return; } if (fflush(lockfile) == EOF) { (*report)("fflush() to pid file '%s' failed", filename); (void)fclose(lockfile); cleanup_pidfile(); return; } (void)fclose(lockfile); }
static int flush_one_file(const char *queue_id, VSTRING *queue_file, struct utimbuf * tbuf, int how) { const char *myname = "flush_one_file"; const char *queue_name; const char *path; /* * Some other instance of this program may flush some logfile and may * just have moved this queue file to the incoming queue. */ for (queue_name = MAIL_QUEUE_DEFERRED; /* see below */ ; queue_name = MAIL_QUEUE_INCOMING) { path = mail_queue_path(queue_file, queue_name, queue_id); if (utime(path, tbuf) == 0) break; if (errno != ENOENT) msg_warn("%s: update %s time stamps: %m", myname, path); if (STREQ(queue_name, MAIL_QUEUE_INCOMING)) return (0); } /* * With the UNTHROTTLE_AFTER strategy, we leave it up to the queue * manager to unthrottle transports and queues as it reads recipients * from a queue file. We request this unthrottle operation by setting the * group read permission bit. * * Note: we must avoid using chmod(). It is not only slower than fchmod() * but it is also less secure. With chmod(), an attacker could repeatedly * send requests to the flush server and trick it into changing * permissions of non-queue files, by exploiting a race condition. * * We use safe_open() because we don't validate the file content before * modifying the file status. */ if (how & UNTHROTTLE_AFTER) { VSTRING *why; struct stat st; VSTREAM *fp; for (why = vstring_alloc(1); /* see below */ ; queue_name = MAIL_QUEUE_INCOMING, path = mail_queue_path(queue_file, queue_name, queue_id)) { if ((fp = safe_open(path, O_RDWR, 0, &st, -1, -1, why)) != 0) break; if (errno != ENOENT) msg_warn("%s: open %s: %s", myname, path, STR(why)); if (errno != ENOENT || STREQ(queue_name, MAIL_QUEUE_INCOMING)) { vstring_free(why); return (0); } } vstring_free(why); if ((st.st_mode & MAIL_QUEUE_STAT_READY) != MAIL_QUEUE_STAT_READY) { (void) vstream_fclose(fp); return (0); } if (fchmod(vstream_fileno(fp), st.st_mode | MAIL_QUEUE_STAT_UNTHROTTLE) < 0) msg_warn("%s: fchmod %s: %m", myname, path); (void) vstream_fclose(fp); } /* * Move the file to the incoming queue, if it isn't already there. */ if (STREQ(queue_name, MAIL_QUEUE_INCOMING) == 0 && mail_queue_rename(queue_id, queue_name, MAIL_QUEUE_INCOMING) < 0 && errno != ENOENT) msg_warn("%s: rename from %s to %s: %m", path, queue_name, MAIL_QUEUE_INCOMING); /* * If we got here, we achieved something, so let's claim succes. */ return (1); }
SHMem * SHMem::initSegment(const char *name, int size, bool &init) { bool needInit = true; /* big enough to hold a uid_t value in decimal */ /* decimal digits = ceiling(log10(uid_t_max)); */ /* log10(uid_t_max) = log256(uid_t_max)/log256(10); */ /* log256(uid_t_max) = sizeof(uid_t); */ /* log10(256) just greater than .41 */ /* so decimal_digits = (sizeof(uid_t)*100 +40)/41 */ #define UID_DIGITS (((sizeof(uid_t)*100)+40)/41) char uid_str[UID_DIGITS+2]; /* 1 for '-', 1 for null */ init = 0; SHMemData *shmemData = new SHMemData; if (!shmemData ) { // applications know we failed because they will get a NULL address // from getSHMemAddr. return NULL; } int mask = umask(0); int ret = mkdir (MEMSEGPATH, 01777); umask(mask); if ((ret == -1) && (errno != EEXIST)) { delete shmemData; return NULL; } /* 1 for the '/', one for the '-' and one for the null */ shmemData->path = new char [sizeof(MEMSEGPATH)+strlen(name)+UID_DIGITS+3]; if (shmemData->path == NULL) { delete shmemData; return NULL; } memcpy(shmemData->path,MEMSEGPATH, sizeof(MEMSEGPATH)); shmemData->path[sizeof(MEMSEGPATH)-1] = '/'; strcpy(&shmemData->path[sizeof(MEMSEGPATH)],name); sprintf(uid_str, "-%u",getuid()); strcat(shmemData->path,uid_str); int mode = 0600; shmemData->fd = open(shmemData->path, O_CREAT|O_RDWR|O_EXCL|O_APPEND|O_EXLOCK, mode); if (shmemData->fd >= 0) { char *buf; int len = size+RESERVED_OFFSET; int ret; buf = (char *)calloc(1,len); if (!buf) { unlink(shmemData->path); #ifdef FULL_CLEANUP flock(shmemData->fd, LOCK_UN); #endif delete shmemData; return NULL; } ret = write(shmemData->fd,buf,len); if (ret != len) { unlink(shmemData->path); #ifdef FULL_CLEANUP flock(shmemData->fd, LOCK_UN); #endif delete shmemData; return NULL; } free(buf); } else if (errno == EEXIST) { needInit = false; shmemData->fd = safe_open(shmemData->path,O_RDWR|O_EXLOCK, mode, size+RESERVED_OFFSET); } if (shmemData->fd < 0) { delete shmemData; return NULL; } shmemData->addr = (char *) mmap(0, size+RESERVED_OFFSET, PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED|MAP_INHERIT, shmemData->fd, 0); if (shmemData->addr == NULL) { if (needInit) { unlink(shmemData->path); } #ifdef FULL_CLEANUP flock(shmemData->fd, LOCK_UN); #endif delete shmemData; return NULL; } shmemData->size = size; #ifdef FULL_CLEANUP (*(unsigned long *)shmemData->addr)++; flock(shmemData->fd, LOCK_UN); #endif init = needInit; SHMem *memseg; memseg = new SHMem(); if (!memseg) { delete shmemData; return NULL; } memseg->shmemData = shmemData; return memseg; }
NORETURN trigger_server_main(int argc, char **argv, TRIGGER_SERVER_FN service,...) { char *myname = "trigger_server_main"; char *root_dir = 0; char *user_name = 0; int debug_me = 0; char *service_name = basename(argv[0]); VSTREAM *stream = 0; int delay; int c; int socket_count = 1; int fd; va_list ap; MAIL_SERVER_INIT_FN pre_init = 0; MAIL_SERVER_INIT_FN post_init = 0; MAIL_SERVER_LOOP_FN loop = 0; int key; char buf[TRIGGER_BUF_SIZE]; int len; char *transport = 0; char *lock_path; VSTRING *why; int alone = 0; int zerolimit = 0; WATCHDOG *watchdog; char *oval; /* * Process environment options as early as we can. */ if (getenv(CONF_ENV_VERB)) msg_verbose = 1; if (getenv(CONF_ENV_DEBUG)) debug_me = 1; /* * Don't die when a process goes away unexpectedly. */ signal(SIGPIPE, SIG_IGN); /* * Don't die for frivolous reasons. */ #ifdef SIGXFSZ signal(SIGXFSZ, SIG_IGN); #endif /* * May need this every now and then. */ var_procname = mystrdup(basename(argv[0])); set_mail_conf_str(VAR_PROCNAME, var_procname); /* * Initialize logging and exit handler. Do the syslog first, so that its * initialization completes before we enter the optional chroot jail. */ msg_syslog_init(mail_task(var_procname), LOG_PID, LOG_FACILITY); if (msg_verbose) msg_info("daemon started"); /* * Initialize from the configuration file. Allow command-line options to * override compiled-in defaults or configured parameter values. */ mail_conf_suck(); /* * Register dictionaries that use higher-level interfaces and protocols. */ mail_dict_init(); /* * Pick up policy settings from master process. Shut up error messages to * stderr, because no-one is going to see them. */ opterr = 0; while ((c = GETOPT(argc, argv, "cDi:lm:n:o:s:St:uvzZ")) > 0) { switch (c) { case 'c': root_dir = "setme"; break; case 'D': debug_me = 1; break; case 'i': mail_conf_update(VAR_MAX_IDLE, optarg); break; case 'l': alone = 1; break; case 'm': mail_conf_update(VAR_MAX_USE, optarg); break; case 'n': service_name = optarg; break; case 'o': if ((oval = split_at(optarg, '=')) == 0) oval = ""; mail_conf_update(optarg, oval); break; case 's': if ((socket_count = atoi(optarg)) <= 0) msg_fatal("invalid socket_count: %s", optarg); break; case 'S': stream = VSTREAM_IN; break; case 't': transport = optarg; break; case 'u': user_name = "setme"; break; case 'v': msg_verbose++; break; case 'z': zerolimit = 1; break; case 'Z': msg_debug++; break; default: msg_fatal("invalid option: %c", c); break; } } /* * Initialize generic parameters. */ mail_params_init(); /* * Application-specific initialization. */ va_start(ap, service); while ((key = va_arg(ap, int)) != 0) { switch (key) { case MAIL_SERVER_INT_TABLE: get_mail_conf_int_table(va_arg(ap, CONFIG_INT_TABLE *)); break; case MAIL_SERVER_STR_TABLE: get_mail_conf_str_table(va_arg(ap, CONFIG_STR_TABLE *)); break; case MAIL_SERVER_BOOL_TABLE: get_mail_conf_bool_table(va_arg(ap, CONFIG_BOOL_TABLE *)); break; case MAIL_SERVER_TIME_TABLE: get_mail_conf_time_table(va_arg(ap, CONFIG_TIME_TABLE *)); break; case MAIL_SERVER_RAW_TABLE: get_mail_conf_raw_table(va_arg(ap, CONFIG_RAW_TABLE *)); break; case MAIL_SERVER_PRE_INIT: pre_init = va_arg(ap, MAIL_SERVER_INIT_FN); break; case MAIL_SERVER_POST_INIT: post_init = va_arg(ap, MAIL_SERVER_INIT_FN); break; case MAIL_SERVER_LOOP: loop = va_arg(ap, MAIL_SERVER_LOOP_FN); break; case MAIL_SERVER_EXIT: trigger_server_onexit = va_arg(ap, MAIL_SERVER_EXIT_FN); break; case MAIL_SERVER_PRE_ACCEPT: trigger_server_pre_accept = va_arg(ap, MAIL_SERVER_ACCEPT_FN); break; case MAIL_SERVER_IN_FLOW_DELAY: trigger_server_in_flow_delay = 1; break; case MAIL_SERVER_SOLITARY: if (!alone) msg_fatal("service %s requires a process limit of 1", service_name); break; case MAIL_SERVER_UNLIMITED: if (!zerolimit) msg_fatal("service %s requires a process limit of 0", service_name); break; default: msg_panic("%s: unknown argument type: %d", myname, key); } } va_end(ap); if (root_dir) root_dir = var_queue_dir; if (user_name) user_name = var_mail_owner; /* * If not connected to stdin, stdin must not be a terminal. */ if (stream == 0 && isatty(STDIN_FILENO)) { msg_vstream_init(var_procname, VSTREAM_ERR); msg_fatal("do not run this command by hand"); } /* * Can options be required? * * XXX Initially this code was implemented with UNIX-domain sockets, but * Solaris <= 2.5 UNIX-domain sockets misbehave hopelessly when the * client disconnects before the server has accepted the connection. * Symptom: the server accept() fails with EPIPE or EPROTO, but the * socket stays readable, so that the program goes into a wasteful loop. * * The initial fix was to use FIFOs, but those turn out to have their own * problems, witness the workarounds in the fifo_listen() routine. * Therefore we support both FIFOs and UNIX-domain sockets, so that the * user can choose whatever works best. * * Well, I give up. Solaris UNIX-domain sockets still don't work properly, * so it will have to limp along with a streams-specific alternative. */ if (stream == 0) { if (transport == 0) msg_fatal("no transport type specified"); if (strcasecmp(transport, MASTER_XPORT_NAME_UNIX) == 0) trigger_server_accept = trigger_server_accept_local; else if (strcasecmp(transport, MASTER_XPORT_NAME_FIFO) == 0) trigger_server_accept = trigger_server_accept_fifo; else msg_fatal("unsupported transport type: %s", transport); } /* * Optionally start the debugger on ourself. */ if (debug_me) debug_process(); /* * Traditionally, BSD select() can't handle multiple processes selecting * on the same socket, and wakes up every process in select(). See TCP/IP * Illustrated volume 2 page 532. We avoid select() collisions with an * external lock file. */ if (stream == 0 && !alone) { lock_path = concatenate(DEF_PID_DIR, "/", transport, ".", service_name, (char *) 0); why = vstring_alloc(1); if ((trigger_server_lock = safe_open(lock_path, O_CREAT | O_RDWR, 0600, (struct stat *) 0, -1, -1, why)) == 0) msg_fatal("open lock file %s: %s", lock_path, vstring_str(why)); close_on_exec(vstream_fileno(trigger_server_lock), CLOSE_ON_EXEC); myfree(lock_path); vstring_free(why); } /* * Set up call-back info. */ trigger_server_service = service; trigger_server_name = service_name; trigger_server_argv = argv + optind; /* * Run pre-jail initialization. */ if (chdir(var_queue_dir) < 0) msg_fatal("chdir(\"%s\"): %m", var_queue_dir); if (pre_init) pre_init(trigger_server_name, trigger_server_argv); /* * Optionally, restrict the damage that this process can do. */ resolve_local_init(); chroot_uid(root_dir, user_name); /* * Run post-jail initialization. */ if (post_init) post_init(trigger_server_name, trigger_server_argv); /* * Are we running as a one-shot server with the client connection on * standard input? */ if (stream != 0) { if ((len = read(vstream_fileno(stream), buf, sizeof(buf))) <= 0) msg_fatal("read: %m"); service(buf, len, trigger_server_name, trigger_server_argv); vstream_fflush(stream); trigger_server_exit(); } /* * Running as a semi-resident server. Service connection requests. * Terminate when we have serviced a sufficient number of clients, when * no-one has been talking to us for a configurable amount of time, or * when the master process terminated abnormally. */ if (var_idle_limit > 0) event_request_timer(trigger_server_timeout, (char *) 0, var_idle_limit); for (fd = MASTER_LISTEN_FD; fd < MASTER_LISTEN_FD + socket_count; fd++) { event_enable_read(fd, trigger_server_accept, CAST_INT_TO_CHAR_PTR(fd)); close_on_exec(fd, CLOSE_ON_EXEC); } event_enable_read(MASTER_STATUS_FD, trigger_server_abort, (char *) 0); close_on_exec(MASTER_STATUS_FD, CLOSE_ON_EXEC); close_on_exec(MASTER_FLOW_READ, CLOSE_ON_EXEC); close_on_exec(MASTER_FLOW_WRITE, CLOSE_ON_EXEC); watchdog = watchdog_create(1000, (WATCHDOG_FN) 0, (char *) 0); /* * The event loop, at last. */ while (var_use_limit == 0 || use_count < var_use_limit) { if (trigger_server_lock != 0) { watchdog_stop(watchdog); if (myflock(vstream_fileno(trigger_server_lock), INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) < 0) msg_fatal("select lock: %m"); } watchdog_start(watchdog); delay = loop ? loop(trigger_server_name, trigger_server_argv) : -1; event_loop(delay); } trigger_server_exit(); }
int CopyRegularFileNet(const char *source, const char *dest, off_t size, bool encrypt, AgentConnection *conn) { char *buf, workbuf[CF_BUFSIZE], cfchangedstr[265]; const int buf_size = 2048; off_t n_read_total = 0; EVP_CIPHER_CTX crypto_ctx; /* We encrypt only for CLASSIC protocol. The TLS protocol is always over * encrypted layer, so it does not support encrypted (S*) commands. */ encrypt = encrypt && conn->conn_info->protocol == CF_PROTOCOL_CLASSIC; if (encrypt) { return EncryptCopyRegularFileNet(source, dest, size, conn); } snprintf(cfchangedstr, 255, "%s%s", CF_CHANGEDSTR1, CF_CHANGEDSTR2); if ((strlen(dest) > CF_BUFSIZE - 20)) { Log(LOG_LEVEL_ERR, "Filename too long"); return false; } unlink(dest); /* To avoid link attacks */ int dd = safe_open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL | O_BINARY, 0600); if (dd == -1) { Log(LOG_LEVEL_ERR, "Copy from server '%s' to destination '%s' failed (open: %s)", conn->this_server, dest, GetErrorStr()); unlink(dest); return false; } workbuf[0] = '\0'; int tosend = snprintf(workbuf, CF_BUFSIZE, "GET %d %s", buf_size, source); if (tosend <= 0 || tosend >= CF_BUFSIZE) { Log(LOG_LEVEL_ERR, "Failed to compose GET command for file %s", source); close(dd); return false; } /* Send proposition C0 */ if (SendTransaction(conn->conn_info, workbuf, tosend, CF_DONE) == -1) { Log(LOG_LEVEL_ERR, "Couldn't send GET command"); close(dd); return false; } buf = xmalloc(CF_BUFSIZE + sizeof(int)); /* Note CF_BUFSIZE not buf_size !! */ Log(LOG_LEVEL_VERBOSE, "Copying remote file '%s:%s', expecting %jd bytes", conn->this_server, source, (intmax_t)size); n_read_total = 0; while (n_read_total < size) { int toget = MIN(size - n_read_total, buf_size); assert(toget != 0); /* Stage C1 - receive */ int n_read; switch(conn->conn_info->protocol) { case CF_PROTOCOL_CLASSIC: n_read = RecvSocketStream(conn->conn_info->sd, buf, toget); break; case CF_PROTOCOL_TLS: n_read = TLSRecv(conn->conn_info->ssl, buf, toget); break; default: UnexpectedError("CopyRegularFileNet: ProtocolVersion %d!", conn->conn_info->protocol); n_read = -1; } if (n_read <= 0) { /* This may happen on race conditions, where the file has shrunk * since we asked for its size in SYNCH ... STAT source */ Log(LOG_LEVEL_ERR, "Error in client-server stream, has %s:%s shrunk? (code %d)", conn->this_server, source, n_read); close(dd); free(buf); return false; } /* If the first thing we get is an error message, break. */ if ((n_read_total == 0) && (strncmp(buf, CF_FAILEDSTR, strlen(CF_FAILEDSTR)) == 0)) { Log(LOG_LEVEL_INFO, "Network access to '%s:%s' denied", conn->this_server, source); close(dd); free(buf); return false; } if (strncmp(buf, cfchangedstr, strlen(cfchangedstr)) == 0) { Log(LOG_LEVEL_INFO, "Source '%s:%s' changed while copying", conn->this_server, source); close(dd); free(buf); return false; } /* Check for mismatch between encryption here and on server. */ int value = -1; sscanf(buf, "t %d", &value); if ((value > 0) && (strncmp(buf + CF_INBAND_OFFSET, "BAD: ", 5) == 0)) { Log(LOG_LEVEL_INFO, "Network access to cleartext '%s:%s' denied", conn->this_server, source); close(dd); free(buf); return false; } if (!FSWrite(dest, dd, buf, n_read)) { Log(LOG_LEVEL_ERR, "Local disk write failed copying '%s:%s' to '%s'. (FSWrite: %s)", conn->this_server, source, dest, GetErrorStr()); if (conn) { conn->error = true; } free(buf); unlink(dest); close(dd); FlushFileStream(conn->conn_info->sd, size - n_read_total); EVP_CIPHER_CTX_cleanup(&crypto_ctx); return false; } n_read_total += n_read; } /* If the file ends with a `hole', something needs to be written at the end. Otherwise the kernel would truncate the file at the end of the last write operation. Write a null character and truncate it again. */ if (ftruncate(dd, n_read_total) < 0) { Log(LOG_LEVEL_ERR, "Copy failed (no space?) while copying '%s' from network '%s'", dest, GetErrorStr()); free(buf); unlink(dest); close(dd); FlushFileStream(conn->conn_info->sd, size - n_read_total); return false; } close(dd); free(buf); return true; }
int main (int argc,char *argv[]) { const char *progname,*filename = NULL,*device = NULL; int i,flags = FLAG_NONE; ssize_t result; size_t size,written; struct mtd_info_user mtd; struct erase_info_user erase; struct stat filestat; unsigned char src[BUFSIZE],dest[BUFSIZE]; (progname = strrchr (argv[0],'/')) ? progname++ : (progname = argv[0]); /********************* * parse cmd-line *****************/ for (;;) { int option_index = 0; static const char *short_options = "hv"; static const struct option long_options[] = { {"help", no_argument, 0, 'h'}, {"verbose", no_argument, 0, 'v'}, {0, 0, 0, 0}, }; int c = getopt_long(argc, argv, short_options, long_options, &option_index); if (c == EOF) { break; } switch (c) { case 'h': flags |= FLAG_HELP; DEBUG("Got FLAG_HELP\n"); break; case 'v': flags |= FLAG_VERBOSE; DEBUG("Got FLAG_VERBOSE\n"); break; default: DEBUG("Unknown parameter: %s\n",argv[option_index]); showusage (progname,true); } } if (optind+2 == argc) { flags |= FLAG_FILENAME; filename = argv[optind]; DEBUG("Got filename: %s\n",filename); flags |= FLAG_DEVICE; device = argv[optind+1]; DEBUG("Got device: %s\n",device); } if (flags & FLAG_HELP || progname == NULL || device == NULL) showusage (progname,flags != FLAG_HELP); atexit (cleanup); /* get some info about the flash device */ dev_fd = safe_open (device,O_SYNC | O_RDWR); if (ioctl (dev_fd,MEMGETINFO,&mtd) < 0) { DEBUG("ioctl(): %m\n"); log_printf (LOG_ERROR,"This doesn't seem to be a valid MTD flash device!\n"); exit (EXIT_FAILURE); } /* get some info about the file we want to copy */ fil_fd = safe_open (filename,O_RDONLY); if (fstat (fil_fd,&filestat) < 0) { log_printf (LOG_ERROR,"While trying to get the file status of %s: %m\n",filename); exit (EXIT_FAILURE); } /* does it fit into the device/partition? */ if (filestat.st_size > mtd.size) { log_printf (LOG_ERROR,"%s won't fit into %s!\n",filename,device); exit (EXIT_FAILURE); } /***************************************************** * erase enough blocks so that we can write the file * *****************************************************/ #warning "Check for smaller erase regions" erase.start = 0; erase.length = (filestat.st_size + mtd.erasesize - 1) / mtd.erasesize; erase.length *= mtd.erasesize; if (flags & FLAG_VERBOSE) { /* if the user wants verbose output, erase 1 block at a time and show him/her what's going on */ int blocks = erase.length / mtd.erasesize; erase.length = mtd.erasesize; log_printf (LOG_NORMAL,"Erasing blocks: 0/%d (0%%)",blocks); for (i = 1; i <= blocks; i++) { log_printf (LOG_NORMAL,"\rErasing blocks: %d/%d (%d%%)",i,blocks,PERCENTAGE (i,blocks)); if (ioctl (dev_fd,MEMERASE,&erase) < 0) { log_printf (LOG_NORMAL,"\n"); log_printf (LOG_ERROR, "While erasing blocks 0x%.8x-0x%.8x on %s: %m\n", (unsigned int) erase.start,(unsigned int) (erase.start + erase.length),device); exit (EXIT_FAILURE); } erase.start += mtd.erasesize; } log_printf (LOG_NORMAL,"\rErasing blocks: %d/%d (100%%)\n",blocks,blocks); } else { /* if not, erase the whole chunk in one shot */ if (ioctl (dev_fd,MEMERASE,&erase) < 0) { log_printf (LOG_ERROR, "While erasing blocks from 0x%.8x-0x%.8x on %s: %m\n", (unsigned int) erase.start,(unsigned int) (erase.start + erase.length),device); exit (EXIT_FAILURE); } } DEBUG("Erased %u / %luk bytes\n",erase.length,filestat.st_size); /********************************** * write the entire file to flash * **********************************/ if (flags & FLAG_VERBOSE) log_printf (LOG_NORMAL,"Writing data: 0k/%luk (0%%)",KB (filestat.st_size)); size = filestat.st_size; i = BUFSIZE; written = 0; while (size) { if (size < BUFSIZE) i = size; if (flags & FLAG_VERBOSE) log_printf (LOG_NORMAL,"\rWriting data: %dk/%luk (%lu%%)", KB (written + i), KB (filestat.st_size), PERCENTAGE (written + i,filestat.st_size)); /* read from filename */ safe_read (fil_fd,filename,src,i,flags & FLAG_VERBOSE); /* write to device */ result = write (dev_fd,src,i); if (i != result) { if (flags & FLAG_VERBOSE) log_printf (LOG_NORMAL,"\n"); if (result < 0) { log_printf (LOG_ERROR, "While writing data to 0x%.8x-0x%.8x on %s: %m\n", written,written + i,device); exit (EXIT_FAILURE); } log_printf (LOG_ERROR, "Short write count returned while writing to x%.8x-0x%.8x on %s: %d/%lu bytes written to flash\n", written,written + i,device,written + result,filestat.st_size); exit (EXIT_FAILURE); } written += i; size -= i; } if (flags & FLAG_VERBOSE) log_printf (LOG_NORMAL, "\rWriting data: %luk/%luk (100%%)\n", KB (filestat.st_size), KB (filestat.st_size)); DEBUG("Wrote %d / %luk bytes\n",written,filestat.st_size); /********************************** * verify that flash == file data * **********************************/ safe_rewind (fil_fd,filename); safe_rewind (dev_fd,device); size = filestat.st_size; i = BUFSIZE; written = 0; if (flags & FLAG_VERBOSE) log_printf (LOG_NORMAL,"Verifying data: 0k/%luk (0%%)",KB (filestat.st_size)); while (size) { if (size < BUFSIZE) i = size; if (flags & FLAG_VERBOSE) log_printf (LOG_NORMAL, "\rVerifying data: %dk/%luk (%lu%%)", KB (written + i), KB (filestat.st_size), PERCENTAGE (written + i,filestat.st_size)); /* read from filename */ safe_read (fil_fd,filename,src,i,flags & FLAG_VERBOSE); /* read from device */ safe_read (dev_fd,device,dest,i,flags & FLAG_VERBOSE); /* compare buffers */ if (memcmp (src,dest,i)) { log_printf (LOG_ERROR, "File does not seem to match flash data. First mismatch at 0x%.8x-0x%.8x\n", written,written + i); exit (EXIT_FAILURE); } written += i; size -= i; } if (flags & FLAG_VERBOSE) log_printf (LOG_NORMAL, "\rVerifying data: %luk/%luk (100%%)\n", KB (filestat.st_size), KB (filestat.st_size)); DEBUG("Verified %d / %luk bytes\n",written,filestat.st_size); exit (EXIT_SUCCESS); }
int EncryptCopyRegularFileNet(const char *source, const char *dest, off_t size, AgentConnection *conn) { int dd, blocksize = 2048, n_read = 0, towrite, plainlen, more = true, finlen, cnt = 0; int tosend, cipherlen = 0; char *buf, in[CF_BUFSIZE], out[CF_BUFSIZE], workbuf[CF_BUFSIZE], cfchangedstr[265]; unsigned char iv[32] = { 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8 }; long n_read_total = 0; EVP_CIPHER_CTX crypto_ctx; snprintf(cfchangedstr, 255, "%s%s", CF_CHANGEDSTR1, CF_CHANGEDSTR2); if ((strlen(dest) > CF_BUFSIZE - 20)) { Log(LOG_LEVEL_ERR, "Filename too long"); return false; } unlink(dest); /* To avoid link attacks */ if ((dd = safe_open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL | O_BINARY, 0600)) == -1) { Log(LOG_LEVEL_ERR, "Copy from server '%s' to destination '%s' failed (open: %s)", conn->this_server, dest, GetErrorStr()); unlink(dest); return false; } if (size == 0) { // No sense in copying an empty file close(dd); return true; } workbuf[0] = '\0'; EVP_CIPHER_CTX_init(&crypto_ctx); snprintf(in, CF_BUFSIZE - CF_PROTO_OFFSET, "GET dummykey %s", source); cipherlen = EncryptString(conn->encryption_type, in, out, conn->session_key, strlen(in) + 1); snprintf(workbuf, CF_BUFSIZE, "SGET %4d %4d", cipherlen, blocksize); memcpy(workbuf + CF_PROTO_OFFSET, out, cipherlen); tosend = cipherlen + CF_PROTO_OFFSET; /* Send proposition C0 - query */ if (SendTransaction(conn->conn_info, workbuf, tosend, CF_DONE) == -1) { Log(LOG_LEVEL_ERR, "Couldn't send data. (SendTransaction: %s)", GetErrorStr()); close(dd); return false; } buf = xmalloc(CF_BUFSIZE + sizeof(int)); n_read_total = 0; while (more) { if ((cipherlen = ReceiveTransaction(conn->conn_info, buf, &more)) == -1) { free(buf); return false; } cnt++; /* If the first thing we get is an error message, break. */ if ((n_read_total == 0) && (strncmp(buf + CF_INBAND_OFFSET, CF_FAILEDSTR, strlen(CF_FAILEDSTR)) == 0)) { Log(LOG_LEVEL_INFO, "Network access to '%s:%s' denied", conn->this_server, source); close(dd); free(buf); return false; } if (strncmp(buf + CF_INBAND_OFFSET, cfchangedstr, strlen(cfchangedstr)) == 0) { Log(LOG_LEVEL_INFO, "Source '%s:%s' changed while copying", conn->this_server, source); close(dd); free(buf); return false; } EVP_DecryptInit_ex(&crypto_ctx, CfengineCipher(CfEnterpriseOptions()), NULL, conn->session_key, iv); if (!EVP_DecryptUpdate(&crypto_ctx, workbuf, &plainlen, buf, cipherlen)) { close(dd); free(buf); return false; } if (!EVP_DecryptFinal_ex(&crypto_ctx, workbuf + plainlen, &finlen)) { close(dd); free(buf); return false; } towrite = n_read = plainlen + finlen; n_read_total += n_read; if (!FSWrite(dest, dd, workbuf, towrite)) { Log(LOG_LEVEL_ERR, "Local disk write failed copying '%s:%s' to '%s:%s'", conn->this_server, source, dest, GetErrorStr()); if (conn) { conn->error = true; } free(buf); unlink(dest); close(dd); EVP_CIPHER_CTX_cleanup(&crypto_ctx); return false; } } /* If the file ends with a `hole', something needs to be written at the end. Otherwise the kernel would truncate the file at the end of the last write operation. Write a null character and truncate it again. */ if (ftruncate(dd, n_read_total) < 0) { Log(LOG_LEVEL_ERR, "Copy failed (no space?) while copying '%s' from network '%s'", dest, GetErrorStr()); free(buf); unlink(dest); close(dd); EVP_CIPHER_CTX_cleanup(&crypto_ctx); return false; } close(dd); free(buf); EVP_CIPHER_CTX_cleanup(&crypto_ctx); return true; }
MBOX *mbox_open(const char *path, int flags, mode_t mode, struct stat * st, uid_t chown_uid, gid_t chown_gid, int lock_style, const char *def_dsn, DSN_BUF *why) { struct stat local_statbuf; MBOX *mp; int locked = 0; VSTREAM *fp; if (st == 0) st = &local_statbuf; /* * If this is a regular file, create a dotlock file. This locking method * does not work well over NFS, but it is better than some alternatives. * With NFS, creating files atomically is a problem, and a successful * operation can fail with EEXIST. * * If filename.lock can't be created for reasons other than "file exists", * issue only a warning if the application says it is non-fatal. This is * for bass-awkward compatibility with existing installations that * deliver to files in non-writable directories. * * We dot-lock the file before opening, so we must avoid doing silly things * like dot-locking /dev/null. Fortunately, deliveries to non-mailbox * files execute with recipient privileges, so we don't have to worry * about creating dotlock files in places where the recipient would not * be able to write. * * Note: we use stat() to follow symlinks, because safe_open() allows the * target to be a root-owned symlink, and we don't want to create dotlock * files for /dev/null or other non-file objects. */ if ((lock_style & MBOX_DOT_LOCK) && (stat(path, st) < 0 || S_ISREG(st->st_mode))) { if (dot_lockfile(path, why->reason) == 0) { locked |= MBOX_DOT_LOCK; } else if (errno == EEXIST) { dsb_status(why, mbox_dsn(EAGAIN, def_dsn)); return (0); } else if (lock_style & MBOX_DOT_LOCK_MAY_FAIL) { msg_warn("%s", vstring_str(why->reason)); } else { dsb_status(why, mbox_dsn(errno, def_dsn)); return (0); } } /* * Open or create the target file. In case of a privileged open, the * privileged user may be attacked with hard/soft link tricks in an * unsafe parent directory. In case of an unprivileged open, the mail * system may be attacked by a malicious user-specified path, or the * unprivileged user may be attacked with hard/soft link tricks in an * unsafe parent directory. Open non-blocking to fend off attacks * involving non-file targets. */ if ((fp = safe_open(path, flags | O_NONBLOCK, mode, st, chown_uid, chown_gid, why->reason)) == 0) { dsb_status(why, mbox_dsn(errno, def_dsn)); if (locked & MBOX_DOT_LOCK) dot_unlockfile(path); return (0); } close_on_exec(vstream_fileno(fp), CLOSE_ON_EXEC); /* * If this is a regular file, acquire kernel locks. flock() locks are not * intended to work across a network; fcntl() locks are supposed to work * over NFS, but in the real world, NFS lock daemons often have serious * problems. */ #define HUNKY_DORY(lock_mask, myflock_style) ((lock_style & (lock_mask)) == 0 \ || deliver_flock(vstream_fileno(fp), (myflock_style), why->reason) == 0) if (S_ISREG(st->st_mode)) { if (HUNKY_DORY(MBOX_FLOCK_LOCK, MYFLOCK_STYLE_FLOCK) && HUNKY_DORY(MBOX_FCNTL_LOCK, MYFLOCK_STYLE_FCNTL)) { locked |= lock_style; } else { dsb_status(why, mbox_dsn(errno, def_dsn)); if (locked & MBOX_DOT_LOCK) dot_unlockfile(path); vstream_fclose(fp); return (0); } } /* * Sanity check: reportedly, GNU POP3D creates a new mailbox file and * deletes the old one. This does not play well with software that opens * the mailbox first and then locks it, such as software that uses FCNTL * or FLOCK locks on open file descriptors (some UNIX systems don't use * dotlock files). * * To detect that GNU POP3D deletes the mailbox file we look at the target * file hard-link count. Note that safe_open() guarantees a hard-link * count of 1, so any change in this count is a sign of trouble. */ if (S_ISREG(st->st_mode) && (fstat(vstream_fileno(fp), st) < 0 || st->st_nlink != 1)) { vstring_sprintf(why->reason, "target file status changed unexpectedly"); dsb_status(why, mbox_dsn(EAGAIN, def_dsn)); msg_warn("%s: file status changed unexpectedly", path); if (locked & MBOX_DOT_LOCK) dot_unlockfile(path); vstream_fclose(fp); return (0); } mp = (MBOX *) mymalloc(sizeof(*mp)); mp->path = mystrdup(path); mp->fp = fp; mp->locked = locked; return (mp); }
int EncryptCopyRegularFileNet(const char *source, const char *dest, off_t size, AgentConnection *conn) { int dd, blocksize = 2048, n_read = 0, plainlen, more = true, finlen, cnt = 0; int tosend, cipherlen = 0; char *buf, in[CF_BUFSIZE], out[CF_BUFSIZE], workbuf[CF_BUFSIZE], cfchangedstr[265]; unsigned char iv[32] = { 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8 }; EVP_CIPHER_CTX crypto_ctx; snprintf(cfchangedstr, 255, "%s%s", CF_CHANGEDSTR1, CF_CHANGEDSTR2); if ((strlen(dest) > CF_BUFSIZE - 20)) { Log(LOG_LEVEL_ERR, "Filename too long"); return false; } unlink(dest); /* To avoid link attacks */ if ((dd = safe_open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL | O_BINARY, 0600)) == -1) { Log(LOG_LEVEL_ERR, "Copy from server '%s' to destination '%s' failed (open: %s)", conn->this_server, dest, GetErrorStr()); unlink(dest); return false; } if (size == 0) { // No sense in copying an empty file close(dd); return true; } workbuf[0] = '\0'; EVP_CIPHER_CTX_init(&crypto_ctx); snprintf(in, CF_BUFSIZE - CF_PROTO_OFFSET, "GET dummykey %s", source); cipherlen = EncryptString(out, sizeof(out), in, strlen(in) + 1, conn->encryption_type, conn->session_key); tosend = cipherlen + CF_PROTO_OFFSET; if(tosend > sizeof(workbuf)) { ProgrammingError("EncryptCopyRegularFileNet: tosend (%d) > workbuf (%ld)", tosend, sizeof(workbuf)); } snprintf(workbuf, CF_BUFSIZE, "SGET %4d %4d", cipherlen, blocksize); memcpy(workbuf + CF_PROTO_OFFSET, out, cipherlen); /* Send proposition C0 - query */ if (SendTransaction(conn->conn_info, workbuf, tosend, CF_DONE) == -1) { Log(LOG_LEVEL_ERR, "Couldn't send data. (SendTransaction: %s)", GetErrorStr()); close(dd); return false; } buf = xmalloc(CF_BUFSIZE + sizeof(int)); bool last_write_made_hole = false; size_t n_wrote_total = 0; while (more) { if ((cipherlen = ReceiveTransaction(conn->conn_info, buf, &more)) == -1) { free(buf); return false; } cnt++; /* If the first thing we get is an error message, break. */ if (n_wrote_total == 0 && strncmp(buf + CF_INBAND_OFFSET, CF_FAILEDSTR, strlen(CF_FAILEDSTR)) == 0) { Log(LOG_LEVEL_INFO, "Network access to '%s:%s' denied", conn->this_server, source); close(dd); free(buf); return false; } if (strncmp(buf + CF_INBAND_OFFSET, cfchangedstr, strlen(cfchangedstr)) == 0) { Log(LOG_LEVEL_INFO, "Source '%s:%s' changed while copying", conn->this_server, source); close(dd); free(buf); return false; } EVP_DecryptInit_ex(&crypto_ctx, CfengineCipher(CfEnterpriseOptions()), NULL, conn->session_key, iv); if (!EVP_DecryptUpdate(&crypto_ctx, workbuf, &plainlen, buf, cipherlen)) { close(dd); free(buf); return false; } if (!EVP_DecryptFinal_ex(&crypto_ctx, workbuf + plainlen, &finlen)) { close(dd); free(buf); return false; } n_read = plainlen + finlen; bool w_ok = FileSparseWrite(dd, workbuf, n_read, &last_write_made_hole); if (!w_ok) { Log(LOG_LEVEL_ERR, "Local disk write failed copying '%s:%s' to '%s'", conn->this_server, source, dest); free(buf); unlink(dest); close(dd); conn->error = true; EVP_CIPHER_CTX_cleanup(&crypto_ctx); return false; } n_wrote_total += n_read; } const bool do_sync = false; bool ret = FileSparseClose(dd, dest, do_sync, n_wrote_total, last_write_made_hole); if (!ret) { unlink(dest); free(buf); EVP_CIPHER_CTX_cleanup(&crypto_ctx); return false; } free(buf); EVP_CIPHER_CTX_cleanup(&crypto_ctx); return true; }
/* returns 1 on success, 0 on error */ int mutt_pipe_attachment (FILE *fp, BODY *b, const char *path, char *outfile) { pid_t thepid; int out = -1; int rv = 0; if (outfile && *outfile) if ((out = safe_open (outfile, O_CREAT | O_EXCL | O_WRONLY)) < 0) { mutt_perror ("open"); return 0; } mutt_endwin (NULL); if (fp) { /* recv case */ STATE s; memset (&s, 0, sizeof (STATE)); if (outfile && *outfile) thepid = mutt_create_filter_fd (path, &s.fpout, NULL, NULL, -1, out, -1); else thepid = mutt_create_filter (path, &s.fpout, NULL, NULL); if (thepid < 0) { mutt_perror _("Can't create filter"); goto bail; } s.fpin = fp; mutt_decode_attachment (b, &s); safe_fclose (&s.fpout); } else { /* send case */ FILE *ifp, *ofp; if ((ifp = fopen (b->filename, "r")) == NULL) { mutt_perror ("fopen"); if (outfile && *outfile) { close (out); unlink (outfile); } return 0; } if (outfile && *outfile) thepid = mutt_create_filter_fd (path, &ofp, NULL, NULL, -1, out, -1); else thepid = mutt_create_filter (path, &ofp, NULL, NULL); if (thepid < 0) { mutt_perror _("Can't create filter"); safe_fclose (&ifp); goto bail; } mutt_copy_stream (ifp, ofp); safe_fclose (&ofp); safe_fclose (&ifp); } rv = 1; bail: if (outfile && *outfile) close (out); /* * check for error exit from child process */ if (mutt_wait_filter (thepid) != 0) rv = 0; if (rv == 0 || option (OPTWAITKEY)) mutt_any_key_to_continue (NULL); return rv; }
/* returns -1 on error, 0 or the return code from mutt_do_pager() on success */ int mutt_view_attachment (FILE *fp, BODY *a, int flag, HEADER *hdr, ATTACHPTR **idx, short idxlen) { char tempfile[_POSIX_PATH_MAX] = ""; char pagerfile[_POSIX_PATH_MAX] = ""; int is_message; int use_mailcap; int use_pipe = 0; int use_pager = 1; char type[STRING]; char command[HUGE_STRING]; char descrip[STRING]; char *fname; rfc1524_entry *entry = NULL; int rc = -1; int unlink_tempfile = 0; is_message = mutt_is_message_type(a->type, a->subtype); if (WithCrypto && is_message && a->hdr && (a->hdr->security & ENCRYPT) && !crypt_valid_passphrase(a->hdr->security)) return (rc); use_mailcap = (flag == M_MAILCAP || (flag == M_REGULAR && mutt_needs_mailcap (a))); snprintf (type, sizeof (type), "%s/%s", TYPE (a), a->subtype); if (use_mailcap) { entry = rfc1524_new_entry (); if (!rfc1524_mailcap_lookup (a, type, entry, 0)) { if (flag == M_REGULAR) { /* fallback to view as text */ rfc1524_free_entry (&entry); mutt_error _("No matching mailcap entry found. Viewing as text."); flag = M_AS_TEXT; use_mailcap = 0; } else goto return_error; } } if (use_mailcap) { if (!entry->command) { mutt_error _("MIME type not defined. Cannot view attachment."); goto return_error; } strfcpy (command, entry->command, sizeof (command)); if (fp) { fname = safe_strdup (a->filename); mutt_sanitize_filename (fname, 1); } else fname = a->filename; if (rfc1524_expand_filename (entry->nametemplate, fname, tempfile, sizeof (tempfile))) { if (fp == NULL && mutt_strcmp(tempfile, a->filename)) { /* send case: the file is already there */ if (safe_symlink (a->filename, tempfile) == -1) { if (mutt_yesorno (_("Can't match nametemplate, continue?"), M_YES) == M_YES) strfcpy (tempfile, a->filename, sizeof (tempfile)); else goto return_error; } else unlink_tempfile = 1; } } else if (fp == NULL) /* send case */ strfcpy (tempfile, a->filename, sizeof (tempfile)); if (fp) { /* recv case: we need to save the attachment to a file */ FREE (&fname); if (mutt_save_attachment (fp, a, tempfile, 0, NULL) == -1) goto return_error; chmod (tempfile, 0400); } use_pipe = rfc1524_expand_command (a, tempfile, type, command, sizeof (command)); use_pager = entry->copiousoutput; } if (use_pager) { if (fp && !use_mailcap && a->filename) { /* recv case */ strfcpy (pagerfile, a->filename, sizeof (pagerfile)); mutt_adv_mktemp (pagerfile, sizeof(pagerfile)); } else mutt_mktemp (pagerfile, sizeof (pagerfile)); } if (use_mailcap) { pid_t thepid = 0; int tempfd = -1, pagerfd = -1; if (!use_pager) mutt_endwin (NULL); if (use_pager || use_pipe) { if (use_pager && ((pagerfd = safe_open (pagerfile, O_CREAT | O_EXCL | O_WRONLY)) == -1)) { mutt_perror ("open"); goto return_error; } if (use_pipe && ((tempfd = open (tempfile, 0)) == -1)) { if(pagerfd != -1) close(pagerfd); mutt_perror ("open"); goto return_error; } if ((thepid = mutt_create_filter_fd (command, NULL, NULL, NULL, use_pipe ? tempfd : -1, use_pager ? pagerfd : -1, -1)) == -1) { if(pagerfd != -1) close(pagerfd); if(tempfd != -1) close(tempfd); mutt_error _("Cannot create filter"); goto return_error; } if (use_pager) { if (a->description) snprintf (descrip, sizeof (descrip), _("---Command: %-20.20s Description: %s"), command, a->description); else snprintf (descrip, sizeof (descrip), _("---Command: %-30.30s Attachment: %s"), command, type); } if ((mutt_wait_filter (thepid) || (entry->needsterminal && option (OPTWAITKEY))) && !use_pager) mutt_any_key_to_continue (NULL); if (tempfd != -1) close (tempfd); if (pagerfd != -1) close (pagerfd); } else { /* interactive command */ if (mutt_system (command) || (entry->needsterminal && option (OPTWAITKEY))) mutt_any_key_to_continue (NULL); } } else { /* Don't use mailcap; the attachment is viewed in the pager */ if (flag == M_AS_TEXT) { /* just let me see the raw data */ if (fp) { /* Viewing from a received message. * * Don't use mutt_save_attachment() because we want to perform charset * conversion since this will be displayed by the internal pager. */ STATE decode_state; memset(&decode_state, 0, sizeof(decode_state)); decode_state.fpout = safe_fopen(pagerfile, "w"); if (!decode_state.fpout) { dprint(1, (debugfile, "mutt_view_attachment:%d safe_fopen(%s) errno=%d %s\n", __LINE__, pagerfile, errno, strerror(errno))); mutt_perror(pagerfile); mutt_sleep(1); goto return_error; } decode_state.fpin = fp; decode_state.flags = M_CHARCONV; mutt_decode_attachment(a, &decode_state); if (fclose(decode_state.fpout) == EOF) dprint(1, (debugfile, "mutt_view_attachment:%d fclose errno=%d %s\n", __LINE__, pagerfile, errno, strerror(errno))); } else { /* in compose mode, just copy the file. we can't use * mutt_decode_attachment() since it assumes the content-encoding has * already been applied */ if (mutt_save_attachment(fp, a, pagerfile, 0, NULL)) goto return_error; } } else { /* Use built-in handler */ set_option (OPTVIEWATTACH); /* disable the "use 'v' to view this part" * message in case of error */ if (mutt_decode_save_attachment (fp, a, pagerfile, M_DISPLAY, 0)) { unset_option (OPTVIEWATTACH); goto return_error; } unset_option (OPTVIEWATTACH); } if (a->description) strfcpy (descrip, a->description, sizeof (descrip)); else if (a->filename) snprintf (descrip, sizeof (descrip), _("---Attachment: %s: %s"), a->filename, type); else snprintf (descrip, sizeof (descrip), _("---Attachment: %s"), type); } /* We only reach this point if there have been no errors */ if (use_pager) { pager_t info; memset (&info, 0, sizeof (info)); info.fp = fp; info.bdy = a; info.ctx = Context; info.idx = idx; info.idxlen = idxlen; info.hdr = hdr; rc = mutt_do_pager (descrip, pagerfile, M_PAGER_ATTACHMENT | (is_message ? M_PAGER_MESSAGE : 0), &info); *pagerfile = '\0'; } else rc = 0; return_error: if (entry) rfc1524_free_entry (&entry); if (fp && tempfile[0]) { /* Restore write permission so mutt_unlink can open the file for writing */ chmod(tempfile, 0600); mutt_unlink (tempfile); } else if (unlink_tempfile) unlink(tempfile); if (pagerfile[0]) mutt_unlink (pagerfile); return rc; }