int stop_wx(const char *name) { char *env_name; const char *pid; env_name = NULL; G_asprintf(&env_name, "MONITOR_%s_PID", G_store_upper(name)); pid = G_getenv_nofatal(env_name); if (!pid) { clean_env(name); G_fatal_error(_("PID file not found")); } #ifdef __MINGW32__ /* TODO */ #else if (kill((pid_t) atoi(pid), SIGTERM) != 0) { /* G_fatal_error(_("Unable to stop monitor <%s>"), name); */ } #endif clean_env(name); return 0; }
int stop_mon(const char *name) { if (!check_mon(name)) { clean_env(name); G_fatal_error(_("Monitor <%s> is not running"), name); } if (strncmp(name, "wx", 2) == 0) return stop_wx(name); return stop(name); }
int stop(const char *name) { char *env_name; const char *env_file; env_name = NULL; G_asprintf(&env_name, "MONITOR_%s_ENVFILE", G_store_upper(name)); env_file = G_getenv_nofatal(env_name); if (!env_file) G_warning(_("Env file not found")); clean_env(name); return 0; }
void process(va_list args, t_env *env, char spec) { int (*funct_array[15])(va_list args, t_env *env); env->specifier = spec; ft_ini_func(funct_array); if (check_flag_minus(env) == 1) env->minus = 1; if (check_flag_zero(env) == 1) env->zero = 1; if (check_flag_space(env) == 1) env->space = 1; if (check_flag_plus(env) == 1) env->plus = 1; if (check_flag_sharp(env) == 1) env->sharp = 1; env->size += funct_array[ft_get_flag(env->specifier)](args, env); clean_env(env); }
void gtk_im_context_set_client_window (GtkIMContext *context, GdkWindow *window) { GtkIMContextClass *klass; g_return_if_fail (GTK_IS_IM_CONTEXT (context)); clean_env(); klass = GTK_IM_CONTEXT_GET_CLASS (context); if (klass->set_client_window) klass->set_client_window (context, window); if(!GDK_IS_WINDOW (window)) return; g_object_set_data(G_OBJECT(context),"window",window); int width = gdk_window_get_width(window); int height = gdk_window_get_height(window); if(width != 0 && height !=0) { gtk_im_context_focus_in(context); local_context = context; } gdk_window_add_filter (window, event_filter, context); }
void gdk_region_get_clipbox (const GdkRegion *region, GdkRectangle *rectangle) { g_return_if_fail (region != NULL); g_return_if_fail (rectangle != NULL); clean_env(); rectangle->x = region->extents.x1; rectangle->y = region->extents.y1; rectangle->width = region->extents.x2 - region->extents.x1; rectangle->height = region->extents.y2 - region->extents.y1; GdkRectangle rect; rect.x = rectangle->x; rect.y = rectangle->y; rect.width = 0; rect.height = rectangle->height; //The caret width is 2; //Maybe sometimes we will make a mistake, but for most of the time, it should be the caret. if(rectangle->width == 2 && GTK_IS_IM_CONTEXT(local_context)) { gtk_im_context_set_cursor_location(local_context, rectangle); } }
int main(int argc, char **argv) { struct stat st; char *slash; int c; int fd; int mode = PQ_MODE_DEFAULT; char *site_to_flush = 0; char *id_to_flush = 0; ARGV *import_env; int bad_site; /* * Fingerprint executables and core dumps. */ MAIL_VERSION_STAMP_ALLOCATE; /* * Be consistent with file permissions. */ umask(022); /* * To minimize confusion, make sure that the standard file descriptors * are open before opening anything else. XXX Work around for 44BSD where * fstat can return EBADF on an open file descriptor. */ for (fd = 0; fd < 3; fd++) if (fstat(fd, &st) == -1 && (close(fd), open("/dev/null", O_RDWR, 0)) != fd) msg_fatal_status(EX_UNAVAILABLE, "open /dev/null: %m"); /* * Initialize. Set up logging, read the global configuration file and * extract configuration information. Set up signal handlers so that we * can clean up incomplete output. */ if ((slash = strrchr(argv[0], '/')) != 0 && slash[1]) argv[0] = slash + 1; msg_vstream_init(argv[0], VSTREAM_ERR); msg_cleanup(unavailable); msg_syslog_init(mail_task("postqueue"), LOG_PID, LOG_FACILITY); set_mail_conf_str(VAR_PROCNAME, var_procname = mystrdup(argv[0])); /* * Check the Postfix library version as soon as we enable logging. */ MAIL_VERSION_CHECK; /* * Parse JCL. This program is set-gid and must sanitize all command-line * parameters. The configuration directory argument is validated by the * mail configuration read routine. Don't do complex things until we have * completed initializations. */ while ((c = GETOPT(argc, argv, "c:fi:ps:v")) > 0) { switch (c) { case 'c': /* non-default configuration */ if (setenv(CONF_ENV_PATH, optarg, 1) < 0) msg_fatal_status(EX_UNAVAILABLE, "out of memory"); break; case 'f': /* flush queue */ if (mode != PQ_MODE_DEFAULT) usage(); mode = PQ_MODE_FLUSH_QUEUE; break; case 'i': /* flush queue file */ if (mode != PQ_MODE_DEFAULT) usage(); mode = PQ_MODE_FLUSH_FILE; id_to_flush = optarg; break; case 'p': /* traditional mailq */ if (mode != PQ_MODE_DEFAULT) usage(); mode = PQ_MODE_MAILQ_LIST; break; case 's': /* flush site */ if (mode != PQ_MODE_DEFAULT) usage(); mode = PQ_MODE_FLUSH_SITE; site_to_flush = optarg; break; case 'v': if (geteuid() == 0) msg_verbose++; break; default: usage(); } } if (argc > optind) usage(); /* * Further initialization... */ mail_conf_read(); /* Re-evaluate mail_task() after reading main.cf. */ msg_syslog_init(mail_task("postqueue"), LOG_PID, LOG_FACILITY); mail_dict_init(); /* proxy, sql, ldap */ get_mail_conf_str_table(str_table); /* * This program is designed to be set-gid, which makes it a potential * target for attack. If not running as root, strip the environment so we * don't have to trust the C library. If running as root, don't strip the * environment so that showq can receive non-default configuration * directory info when the mail system is down. */ if (geteuid() != 0) { import_env = mail_parm_split(VAR_IMPORT_ENVIRON, var_import_environ); clean_env(import_env->argv); argv_free(import_env); } if (chdir(var_queue_dir)) msg_fatal_status(EX_UNAVAILABLE, "chdir %s: %m", var_queue_dir); signal(SIGPIPE, SIG_IGN); /* End of initializations. */ /* * Further input validation. */ if (site_to_flush != 0) { bad_site = 0; if (*site_to_flush == '[') { bad_site = !valid_mailhost_literal(site_to_flush, DONT_GRIPE); } else { bad_site = !valid_hostname(site_to_flush, DONT_GRIPE); } if (bad_site) msg_fatal_status(EX_USAGE, "Cannot flush mail queue - invalid destination: \"%.100s%s\"", site_to_flush, strlen(site_to_flush) > 100 ? "..." : ""); } if (id_to_flush != 0) { if (!mail_queue_id_ok(id_to_flush)) msg_fatal_status(EX_USAGE, "Cannot flush queue ID - invalid name: \"%.100s%s\"", id_to_flush, strlen(id_to_flush) > 100 ? "..." : ""); } /* * Start processing. */ switch (mode) { default: msg_panic("unknown operation mode: %d", mode); /* NOTREACHED */ case PQ_MODE_MAILQ_LIST: show_queue(); exit(0); break; case PQ_MODE_FLUSH_SITE: flush_site(site_to_flush); exit(0); break; case PQ_MODE_FLUSH_FILE: flush_file(id_to_flush); exit(0); break; case PQ_MODE_FLUSH_QUEUE: flush_queue(); exit(0); break; case PQ_MODE_DEFAULT: usage(); /* NOTREACHED */ } }
int main(int argc, char **argv) { static VSTREAM *lock_fp; static VSTREAM *data_lock_fp; VSTRING *lock_path; VSTRING *data_lock_path; off_t inherited_limit; int debug_me = 0; int ch; int fd; int n; int test_lock = 0; VSTRING *why; WATCHDOG *watchdog; ARGV *import_env; /* * Fingerprint executables and core dumps. */ MAIL_VERSION_STAMP_ALLOCATE; /* * Initialize. */ umask(077); /* never fails! */ /* * 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); /* * Strip and save the process name for diagnostics etc. */ var_procname = mystrdup(basename(argv[0])); /* * When running a child process, don't leak any open files that were * leaked to us by our own (privileged) parent process. Descriptors 0-2 * are taken care of after we have initialized error logging. * * Some systems such as AIX have a huge per-process open file limit. In * those cases, limit the search for potential file descriptor leaks to * just the first couple hundred. * * The Debian post-installation script passes an open file descriptor into * the master process and waits forever for someone to close it. Because * of this we have to close descriptors > 2, and pray that doing so does * not break things. */ closefrom(3); /* * Initialize logging and exit handler. */ msg_syslog_init(mail_task(var_procname), LOG_PID, LOG_FACILITY); /* * Check the Postfix library version as soon as we enable logging. */ MAIL_VERSION_CHECK; /* * The mail system must be run by the superuser so it can revoke * privileges for selected operations. That's right - it takes privileges * to toss privileges. */ if (getuid() != 0) msg_fatal("the master command is reserved for the superuser"); if (unsafe() != 0) msg_fatal("the master command must not run as a set-uid process"); /* * Process JCL. */ while ((ch = GETOPT(argc, argv, "c:Dde:tv")) > 0) { switch (ch) { case 'c': if (setenv(CONF_ENV_PATH, optarg, 1) < 0) msg_fatal("out of memory"); break; case 'd': master_detach = 0; break; case 'e': event_request_timer(master_exit_event, (char *) 0, atoi(optarg)); break; case 'D': debug_me = 1; break; case 't': test_lock = 1; break; case 'v': msg_verbose++; break; default: usage(argv[0]); /* NOTREACHED */ } } /* * This program takes no other arguments. */ if (argc > optind) usage(argv[0]); /* * If started from a terminal, get rid of any tty association. This also * means that all errors and warnings must go to the syslog daemon. */ if (master_detach) for (fd = 0; fd < 3; fd++) { (void) close(fd); if (open("/dev/null", O_RDWR, 0) != fd) msg_fatal("open /dev/null: %m"); } /* * Run in a separate process group, so that "postfix stop" can terminate * all MTA processes cleanly. Give up if we can't separate from our * parent process. We're not supposed to blow away the parent. */ if (debug_me == 0 && master_detach != 0 && setsid() == -1 && getsid(0) != getpid()) msg_fatal("unable to set session and process group ID: %m"); /* * Make some room for plumbing with file descriptors. XXX This breaks * when a service listens on many ports. In order to do this right we * must change the master-child interface so that descriptors do not need * to have fixed numbers. * * In a child we need two descriptors for the flow control pipe, one for * child->master status updates and at least one for listening. */ for (n = 0; n < 5; n++) { if (close_on_exec(dup(0), CLOSE_ON_EXEC) < 0) msg_fatal("dup(0): %m"); } /* * Final initializations. Unfortunately, we must read the global Postfix * configuration file after doing command-line processing, so that we get * consistent results when we SIGHUP the server to reload configuration * files. */ master_vars_init(); /* * In case of multi-protocol support. This needs to be done because * master does not invoke mail_params_init() (it was written before that * code existed). */ (void) inet_proto_init(VAR_INET_PROTOCOLS, var_inet_protocols); /* * Environment import filter, to enforce consistent behavior whether * Postfix is started by hand, or at system boot time. */ import_env = argv_split(var_import_environ, ", \t\r\n"); clean_env(import_env->argv); argv_free(import_env); if ((inherited_limit = get_file_limit()) < 0) set_file_limit(OFF_T_MAX); if (chdir(var_queue_dir)) msg_fatal("chdir %s: %m", var_queue_dir); /* * Lock down the master.pid file. In test mode, no file means that it * isn't locked. */ lock_path = vstring_alloc(10); data_lock_path = vstring_alloc(10); why = vstring_alloc(10); vstring_sprintf(lock_path, "%s/%s.pid", DEF_PID_DIR, var_procname); if (test_lock && access(vstring_str(lock_path), F_OK) < 0) exit(0); lock_fp = open_lock(vstring_str(lock_path), O_RDWR | O_CREAT, 0644, why); if (test_lock) exit(lock_fp ? 0 : 1); if (lock_fp == 0) msg_fatal("open lock file %s: %s", vstring_str(lock_path), vstring_str(why)); vstream_fprintf(lock_fp, "%*lu\n", (int) sizeof(unsigned long) * 4, (unsigned long) var_pid); if (vstream_fflush(lock_fp)) msg_fatal("cannot update lock file %s: %m", vstring_str(lock_path)); close_on_exec(vstream_fileno(lock_fp), CLOSE_ON_EXEC); /* * Lock down the Postfix-writable data directory. */ vstring_sprintf(data_lock_path, "%s/%s.lock", var_data_dir, var_procname); set_eugid(var_owner_uid, var_owner_gid); data_lock_fp = open_lock(vstring_str(data_lock_path), O_RDWR | O_CREAT, 0644, why); set_ugid(getuid(), getgid()); if (data_lock_fp == 0) msg_fatal("open lock file %s: %s", vstring_str(data_lock_path), vstring_str(why)); vstream_fprintf(data_lock_fp, "%*lu\n", (int) sizeof(unsigned long) * 4, (unsigned long) var_pid); if (vstream_fflush(data_lock_fp)) msg_fatal("cannot update lock file %s: %m", vstring_str(data_lock_path)); close_on_exec(vstream_fileno(data_lock_fp), CLOSE_ON_EXEC); /* * Clean up. */ vstring_free(why); vstring_free(lock_path); vstring_free(data_lock_path); /* * Optionally start the debugger on ourself. */ if (debug_me) debug_process(); /* * Finish initialization, last part. We must process configuration files * after processing command-line parameters, so that we get consistent * results when we SIGHUP the server to reload configuration files. */ master_config(); master_sigsetup(); master_flow_init(); msg_info("daemon started -- version %s, configuration %s", var_mail_version, var_config_dir); /* * Process events. The event handler will execute the read/write/timer * action routines. Whenever something has happened, see if we received * any signal in the mean time. Although the master process appears to do * multiple things at the same time, it really is all a single thread, so * that there are no concurrency conflicts within the master process. */ #define MASTER_WATCHDOG_TIME 1000 watchdog = watchdog_create(MASTER_WATCHDOG_TIME, (WATCHDOG_FN) 0, (char *) 0); for (;;) { #ifdef HAS_VOLATILE_LOCKS if (myflock(vstream_fileno(lock_fp), INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) < 0) msg_fatal("refresh exclusive lock: %m"); if (myflock(vstream_fileno(data_lock_fp), INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) < 0) msg_fatal("refresh exclusive lock: %m"); #endif watchdog_start(watchdog); /* same as trigger servers */ event_loop(MASTER_WATCHDOG_TIME / 2); if (master_gotsighup) { msg_info("reload -- version %s, configuration %s", var_mail_version, var_config_dir); master_gotsighup = 0; /* this first */ master_vars_init(); /* then this */ master_refresh(); /* then this */ } if (master_gotsigchld) { if (msg_verbose) msg_info("got sigchld"); master_gotsigchld = 0; /* this first */ master_reap_child(); /* then this */ } } }
int main(int argc, char *argv[]) { int userdir = 0; /* ~userdir flag */ uid_t uid; /* user information */ gid_t gid; /* target group placeholder */ char *target_uname; /* target user name */ char *target_gname; /* target group name */ char *target_homedir; /* target home directory */ char *actual_uname; /* actual user name */ char *actual_gname; /* actual group name */ char *prog; /* name of this program */ char *cmd; /* command to be executed */ char cwd[AP_MAXPATH]; /* current working directory */ char dwd[AP_MAXPATH]; /* docroot working directory */ struct passwd *pw; /* password entry holder */ struct group *gr; /* group entry holder */ struct stat dir_info; /* directory info holder */ struct stat prg_info; /* program info holder */ /* * Start with a "clean" environment */ clean_env(); prog = argv[0]; /* * Check existence/validity of the UID of the user * running this program. Error out if invalid. */ uid = getuid(); if ((pw = getpwuid(uid)) == NULL) { log_err("crit: invalid uid: (%ld)\n", uid); exit(102); } /* * See if this is a 'how were you compiled' request, and * comply if so. */ if ((argc > 1) && (! strcmp(argv[1], "-V")) && ((uid == 0) #ifdef _OSD_POSIX /* User name comparisons are case insensitive on BS2000/OSD */ || (! strcasecmp(AP_HTTPD_USER, pw->pw_name))) #else /* _OSD_POSIX */ || (! strcmp(AP_HTTPD_USER, pw->pw_name))) #endif /* _OSD_POSIX */ ) { #ifdef AP_DOC_ROOT fprintf(stderr, " -D AP_DOC_ROOT=\"%s\"\n", AP_DOC_ROOT); #endif #ifdef AP_GID_MIN fprintf(stderr, " -D AP_GID_MIN=%d\n", AP_GID_MIN); #endif #ifdef AP_HTTPD_USER fprintf(stderr, " -D AP_HTTPD_USER=\"%s\"\n", AP_HTTPD_USER); #endif #ifdef AP_LOG_EXEC fprintf(stderr, " -D AP_LOG_EXEC=\"%s\"\n", AP_LOG_EXEC); #endif #ifdef AP_SAFE_PATH fprintf(stderr, " -D AP_SAFE_PATH=\"%s\"\n", AP_SAFE_PATH); #endif #ifdef AP_SUEXEC_UMASK fprintf(stderr, " -D AP_SUEXEC_UMASK=%03o\n", AP_SUEXEC_UMASK); #endif #ifdef AP_UID_MIN fprintf(stderr, " -D AP_UID_MIN=%d\n", AP_UID_MIN); #endif #ifdef AP_USERDIR_SUFFIX fprintf(stderr, " -D AP_USERDIR_SUFFIX=\"%s\"\n", AP_USERDIR_SUFFIX); #endif exit(0); } /* * If there are a proper number of arguments, set * all of them to variables. Otherwise, error out. */ if (argc < 4) { log_err("too few arguments\n"); exit(101); } target_uname = argv[1]; target_gname = argv[2]; cmd = argv[3]; /* * Check to see if the user running this program * is the user allowed to do so as defined in * suexec.h. If not the allowed user, error out. */ #ifdef _OSD_POSIX /* User name comparisons are case insensitive on BS2000/OSD */ if (strcasecmp(AP_HTTPD_USER, pw->pw_name)) { log_err("user mismatch (%s instead of %s)\n", pw->pw_name, AP_HTTPD_USER); exit(103); } #else /*_OSD_POSIX*/ if (strcmp(AP_HTTPD_USER, pw->pw_name)) { log_err("user mismatch (%s instead of %s)\n", pw->pw_name, AP_HTTPD_USER); exit(103); } #endif /*_OSD_POSIX*/ /* * Check for a leading '/' (absolute path) in the command to be executed, * or attempts to back up out of the current directory, * to protect against attacks. If any are * found, error out. Naughty naughty crackers. */ if ((cmd[0] == '/') || (!strncmp(cmd, "../", 3)) || (strstr(cmd, "/../") != NULL)) { log_err("invalid command (%s)\n", cmd); exit(104); } /* * Check to see if this is a ~userdir request. If * so, set the flag, and remove the '~' from the * target username. */ if (!strncmp("~", target_uname, 1)) { target_uname++; userdir = 1; } /* * Error out if the target username is invalid. */ if (strspn(target_uname, "1234567890") != strlen(target_uname)) { if ((pw = getpwnam(target_uname)) == NULL) { log_err("invalid target user name: (%s)\n", target_uname); exit(105); } } else { if ((pw = getpwuid(atoi(target_uname))) == NULL) { log_err("invalid target user id: (%s)\n", target_uname); exit(121); } } /* * Error out if the target group name is invalid. */ if (strspn(target_gname, "1234567890") != strlen(target_gname)) { if ((gr = getgrnam(target_gname)) == NULL) { log_err("invalid target group name: (%s)\n", target_gname); exit(106); } gid = gr->gr_gid; actual_gname = strdup(gr->gr_name); } else { gid = atoi(target_gname); actual_gname = strdup(target_gname); } #ifdef _OSD_POSIX /* * Initialize BS2000 user environment */ { pid_t pid; int status; switch (pid = ufork(target_uname)) { case -1: /* Error */ log_err("failed to setup bs2000 environment for user %s: %s\n", target_uname, strerror(errno)); exit(150); case 0: /* Child */ break; default: /* Father */ while (pid != waitpid(pid, &status, 0)) ; /* @@@ FIXME: should we deal with STOP signals as well? */ if (WIFSIGNALED(status)) { kill (getpid(), WTERMSIG(status)); } exit(WEXITSTATUS(status)); } } #endif /*_OSD_POSIX*/ /* * Save these for later since initgroups will hose the struct */ uid = pw->pw_uid; actual_uname = strdup(pw->pw_name); target_homedir = strdup(pw->pw_dir); /* * Log the transaction here to be sure we have an open log * before we setuid(). */ log_no_err("uid: (%s/%s) gid: (%s/%s) cmd: %s\n", target_uname, actual_uname, target_gname, actual_gname, cmd); /* * Error out if attempt is made to execute as root or as * a UID less than AP_UID_MIN. Tsk tsk. */ if ((uid == 0) || (uid < AP_UID_MIN)) { log_err("cannot run as forbidden uid (%d/%s)\n", uid, cmd); exit(107); } /* * Error out if attempt is made to execute as root group * or as a GID less than AP_GID_MIN. Tsk tsk. */ if ((gid == 0) || (gid < AP_GID_MIN)) { log_err("cannot run as forbidden gid (%d/%s)\n", gid, cmd); exit(108); } /* * Change UID/GID here so that the following tests work over NFS. * * Initialize the group access list for the target user, * and setgid() to the target group. If unsuccessful, error out. */ if (((setgid(gid)) != 0) || (initgroups(actual_uname, gid) != 0)) { log_err("failed to setgid (%ld: %s)\n", gid, cmd); exit(109); } /* * setuid() to the target user. Error out on fail. */ if ((setuid(uid)) != 0) { log_err("failed to setuid (%ld: %s)\n", uid, cmd); exit(110); } /* * Get the current working directory, as well as the proper * document root (dependant upon whether or not it is a * ~userdir request). Error out if we cannot get either one, * or if the current working directory is not in the docroot. * Use chdir()s and getcwd()s to avoid problems with symlinked * directories. Yuck. */ if (getcwd(cwd, AP_MAXPATH) == NULL) { log_err("cannot get current working directory\n"); exit(111); } if (userdir) { if (((chdir(target_homedir)) != 0) || ((chdir(AP_USERDIR_SUFFIX)) != 0) || ((getcwd(dwd, AP_MAXPATH)) == NULL) || ((chdir(cwd)) != 0)) { log_err("cannot get docroot information (%s)\n", target_homedir); exit(112); } } else { if (((chdir(AP_DOC_ROOT)) != 0) || ((getcwd(dwd, AP_MAXPATH)) == NULL) || ((chdir(cwd)) != 0)) { log_err("cannot get docroot information (%s)\n", AP_DOC_ROOT); exit(113); } } if ((strncmp(cwd, dwd, strlen(dwd))) != 0) { log_err("command not in docroot (%s/%s)\n", cwd, cmd); exit(114); } /* * Stat the cwd and verify it is a directory, or error out. */ if (((lstat(cwd, &dir_info)) != 0) || !(S_ISDIR(dir_info.st_mode))) { log_err("cannot stat directory: (%s)\n", cwd); exit(115); } /* * Error out if cwd is writable by others. */ if ((dir_info.st_mode & S_IWOTH) || (dir_info.st_mode & S_IWGRP)) { log_err("directory is writable by others: (%s)\n", cwd); exit(116); } /* * Error out if we cannot stat the program. */ if (((lstat(cmd, &prg_info)) != 0) || (S_ISLNK(prg_info.st_mode))) { log_err("cannot stat program: (%s)\n", cmd); exit(117); } /* * Error out if the program is writable by others. */ if ((prg_info.st_mode & S_IWOTH) || (prg_info.st_mode & S_IWGRP)) { log_err("file is writable by others: (%s/%s)\n", cwd, cmd); exit(118); } /* * Error out if the file is setuid or setgid. */ if ((prg_info.st_mode & S_ISUID) || (prg_info.st_mode & S_ISGID)) { log_err("file is either setuid or setgid: (%s/%s)\n", cwd, cmd); exit(119); } /* * Error out if the target name/group is different from * the name/group of the cwd or the program. */ if ((uid != dir_info.st_uid) || (gid != dir_info.st_gid) || (uid != prg_info.st_uid) || (gid != prg_info.st_gid)) { log_err("target uid/gid (%ld/%ld) mismatch " "with directory (%ld/%ld) or program (%ld/%ld)\n", uid, gid, dir_info.st_uid, dir_info.st_gid, prg_info.st_uid, prg_info.st_gid); exit(120); } /* * Error out if the program is not executable for the user. * Otherwise, she won't find any error in the logs except for * "[error] Premature end of script headers: ..." */ if (!(prg_info.st_mode & S_IXUSR)) { log_err("file has no execute permission: (%s/%s)\n", cwd, cmd); exit(121); } #ifdef AP_SUEXEC_UMASK /* * umask() uses inverse logic; bits are CLEAR for allowed access. */ if ((~AP_SUEXEC_UMASK) & 0022) { log_err("notice: AP_SUEXEC_UMASK of %03o allows " "write permission to group and/or other\n", AP_SUEXEC_UMASK); } umask(AP_SUEXEC_UMASK); #endif /* AP_SUEXEC_UMASK */ /* * Be sure to close the log file so the CGI can't * mess with it. If the exec fails, it will be reopened * automatically when log_err is called. Note that the log * might not actually be open if AP_LOG_EXEC isn't defined. * However, the "log" cell isn't ifdef'd so let's be defensive * and assume someone might have done something with it * outside an ifdef'd AP_LOG_EXEC block. */ if (log != NULL) { fclose(log); log = NULL; } /* * Execute the command, replacing our image with its own. */ #ifdef NEED_HASHBANG_EMUL /* We need the #! emulation when we want to execute scripts */ { extern char **environ; ap_execve(cmd, &argv[3], environ); } #else /*NEED_HASHBANG_EMUL*/ execv(cmd, &argv[3]); #endif /*NEED_HASHBANG_EMUL*/ /* * (I can't help myself...sorry.) * * Uh oh. Still here. Where's the kaboom? There was supposed to be an * EARTH-shattering kaboom! * * Oh well, log the failure and error out. */ log_err("(%d)%s: exec failed (%s)\n", errno, strerror(errno), cmd); exit(255); }
void ft_exit(char *message, t_env *e) { ft_putendl(message); clean_env(e); exit(0); }
int main(int argc, char *argv[]) { int userdir = 0; /* ~userdir flag */ uid_t uid; /* user information */ gid_t gid, primary_gid; /* target group placeholder */ char *target_uname; /* target user name */ char *target_gname; /* target group name */ char *target_homedir; /* target home directory */ char *actual_uname; /* actual user name */ char *actual_gname; /* actual group name */ char *prog; /* name of this program */ char *cmd; /* command to be executed */ char cwd[AP_MAXPATH]; /* current working directory */ char dwd[AP_MAXPATH]; /* docroot working directory */ #ifdef TESTBED char cmdpath[AP_MAXPATH]; /* full path to command */ int grouplist[NGROUPS], ngroups = 0; #endif struct passwd *pw; /* password entry holder */ struct group *gr; /* group entry holder */ struct stat dir_info; /* directory info holder */ struct stat prg_info; /* program info holder */ #ifdef TESTBED /* * For the Testbed, we run this from php3, and it loses all * outout sent to stderr, so make sure it all goes to stdout. */ dup2(1, 2); { int i, max; /* * Close all other descriptors. I think this is the wrong * place for this since a program can be run from the web * server without going through suexec! */ max = getdtablesize(); for (i = 3; i < max; i++) (void) close(i); } #endif /* * If there are a proper number of arguments, set * all of them to variables. Otherwise, error out. */ prog = argv[0]; if (argc < 4) { log_err("alert: too few arguments\n"); exit(101); } target_uname = argv[1]; target_gname = argv[2]; cmd = argv[3]; /* * Check existence/validity of the UID of the user * running this program. Error out if invalid. */ uid = getuid(); if ((pw = getpwuid(uid)) == NULL) { log_err("crit: invalid uid: (%ld)\n", uid); exit(102); } /* * Check to see if the user running this program * is the user allowed to do so as defined in * suexec.h. If not the allowed user, error out. */ #ifdef _OSD_POSIX /* User name comparisons are case insensitive on BS2000/OSD */ if (strcasecmp(HTTPD_USER, pw->pw_name)) { log_err("crit: calling user mismatch (%s instead of %s)\n", pw->pw_name, HTTPD_USER); exit(103); } #else /* _OSD_POSIX */ if (strcmp(HTTPD_USER, pw->pw_name)) { log_err("crit: calling user mismatch (%s instead of %s)\n", pw->pw_name, HTTPD_USER); exit(103); } #endif /* _OSD_POSIX */ /* * Check for a leading '/' (absolute path) in the command to be executed, * or attempts to back up out of the current directory, * to protect against attacks. If any are * found, error out. Naughty naughty crackers. */ if ((cmd[0] == '/') || (!strncmp(cmd, "../", 3)) || (strstr(cmd, "/../") != NULL)) { log_err("error: invalid command (%s)\n", cmd); exit(104); } #ifndef TESTBED /* * Check to see if this is a ~userdir request. If * so, set the flag, and remove the '~' from the * target username. */ if (!strncmp("~", target_uname, 1)) { target_uname++; userdir = 1; } #endif /* * Error out if the target username is invalid. */ if ((pw = getpwnam(target_uname)) == NULL) { log_err("crit: invalid target user name: (%s)\n", target_uname); exit(105); } #ifdef TESTBED { char *cp, *bp, *rname, *temp_gname = strdup(target_gname); gid_t rgid; int i; actual_gname = NULL; bp = temp_gname; while ((bp = strsep(&temp_gname, ",")) != NULL) { if (!*bp) continue; /* * Error out if the target group name is invalid. */ if (strspn(bp, "1234567890") != strlen(bp)) { if ((gr = getgrnam(bp)) == NULL) { log_err("crit: invalid target group name: (%s)\n", bp); exit(106); } rgid = gr->gr_gid; rname = gr->gr_name; } else { rgid = atoi(bp); rname = bp; } /* Watch for duplicates */ for (i = 0; i < ngroups; i++) { if (grouplist[i] == rgid) goto skip; } grouplist[ngroups++] = rgid; /* * Error out if attempt is made to execute as root group * or as a GID less than GID_MIN. Tsk tsk. */ if ((rgid == 0) || (rgid < GID_MIN)) { log_err("crit: cannot run as forbidden gid (%d/%s)\n", gid, cmd); exit(108); } /* see below; need room for primary group in first two slots */ if (ngroups >= (NGROUPS - 2)) { log_err("crit: Too many groups: (%s)\n", bp); exit(106); } if (actual_gname) { cp = (char *) malloc(strlen(actual_gname) + strlen(rname) + 2); strcpy(cp, actual_gname); strcat(cp, ","); strcat(cp, rname); free(actual_gname); actual_gname = cp; } else { actual_gname = strdup(rname); } skip: ; } } #else /* * Error out if the target group name is invalid. */ if (strspn(target_gname, "1234567890") != strlen(target_gname)) { if ((gr = getgrnam(target_gname)) == NULL) { log_err("crit: invalid target group name: (%s)\n", target_gname); exit(106); } gid = gr->gr_gid; actual_gname = strdup(gr->gr_name); } else { gid = atoi(target_gname); actual_gname = strdup(target_gname); } #endif #ifdef _OSD_POSIX /* * Initialize BS2000 user environment */ { pid_t pid; int status; switch (pid = ufork(target_uname)) { case -1: /* Error */ log_err("emerg: failed to setup bs2000 environment for user " "%s: %s\n", target_uname, strerror(errno)); exit(150); case 0: /* Child */ break; default: /* Father */ while (pid != waitpid(pid, &status, 0)) ; /* @@@ FIXME: should we deal with STOP signals as well? */ if (WIFSIGNALED(status)) { kill (getpid(), WTERMSIG(status)); } exit(WEXITSTATUS(status)); } } #endif /* _OSD_POSIX */ /* * Save these for later since initgroups will hose the struct */ uid = pw->pw_uid; primary_gid = pw->pw_gid; actual_uname = strdup(pw->pw_name); target_homedir = strdup(pw->pw_dir); /* * Log the transaction here to be sure we have an open log * before we setuid(). */ { char argbuf[2*BUFSIZ], *bp = argbuf; int i, size = sizeof(argbuf) - 1; *bp = '\0'; for (i = 4; i < argc; i++) { int count = snprintf(bp, size, "%s ", argv[i]); if (count >= size) break; size -= count; bp += count; } log_err("info: (target/actual) uid: (%s/%s) gid: (%s/%s) cmd: %s %s\n", target_uname, actual_uname, target_gname, actual_gname, cmd, argbuf); } /* * Error out if attempt is made to execute as root or as * a UID less than UID_MIN. Tsk tsk. */ if ((uid == 0) || (uid < UID_MIN)) { log_err("crit: cannot run as forbidden uid (%d/%s)\n", uid, cmd); exit(107); } #ifdef TESTBED { gid_t groups[NGROUPS]; int i, idx = 0; groups[idx++] = primary_gid; /* duplicate has something to do with effective gid */ groups[idx++] = primary_gid; /* Move over the grouplist from above. */ for (i = 0; i < ngroups; i++) { if (grouplist[i] != primary_gid) groups[idx++] = grouplist[i]; } if (setgid(primary_gid) != 0) { log_err("emerg: failed to setgid (%ld: %s)\n", primary_gid, cmd); exit(109); } if (setgroups(idx, groups) != 0) { log_err("emerg: failed to setgroups (%s: %s)\n", actual_gname,cmd); exit(109); } } #else /* * Error out if attempt is made to execute as root group * or as a GID less than GID_MIN. Tsk tsk. */ if ((gid == 0) || (gid < GID_MIN)) { log_err("crit: cannot run as forbidden gid (%d/%s)\n", gid, cmd); exit(108); } /* * Change UID/GID here so that the following tests work over NFS. * * Initialize the group access list for the target user, * and setgid() to the target group. If unsuccessful, error out. */ if (((setgid(gid)) != 0) || (initgroups(actual_uname, gid) != 0)) { log_err("emerg: failed to setgid (%ld: %s)\n", gid, cmd); exit(109); } #endif /* * setuid() to the target user. Error out on fail. */ if ((setuid(uid)) != 0) { log_err("emerg: failed to setuid (%ld: %s)\n", uid, cmd); exit(110); } #ifdef TESTBED /* * All programs exist in the testbed bin directory, and we won't be * running those programs from that directory (cwd will not be the * bin directory, but rather someplace else). So, fake things * up so the rest of this code works okay. */ strcpy(cwd, DOC_ROOT); strcpy(cmdpath, DOC_ROOT); strcat(cmdpath, cmd); cmd = cmdpath; #else /* * Get the current working directory, as well as the proper * document root (dependant upon whether or not it is a * ~userdir request). Error out if we cannot get either one, * or if the current working directory is not in the docroot. * Use chdir()s and getcwd()s to avoid problems with symlinked * directories. Yuck. */ if (getcwd(cwd, AP_MAXPATH) == NULL) { log_err("emerg: cannot get current working directory\n"); exit(111); } if (userdir) { if (((chdir(target_homedir)) != 0) || ((chdir(USERDIR_SUFFIX)) != 0) || ((getcwd(dwd, AP_MAXPATH)) == NULL) || ((chdir(cwd)) != 0)) { log_err("emerg: cannot get docroot information (%s)\n", target_homedir); exit(112); } } else { if (((chdir(DOC_ROOT)) != 0) || ((getcwd(dwd, AP_MAXPATH)) == NULL) || ((chdir(cwd)) != 0)) { log_err("emerg: cannot get docroot information (%s)\n", DOC_ROOT); exit(113); } } if ((strncmp(cwd, dwd, strlen(dwd))) != 0) { log_err("error: command not in docroot (%s/%s)\n", cwd, cmd); exit(114); } #endif /* * Stat the cwd and verify it is a directory, or error out. */ if (((lstat(cwd, &dir_info)) != 0) || !(S_ISDIR(dir_info.st_mode))) { log_err("error: cannot stat directory: (%s)\n", cwd); exit(115); } /* * Error out if cwd is writable by others. */ #ifdef TESTBED if (dir_info.st_mode & S_IWOTH) { #else if ((dir_info.st_mode & S_IWOTH) || (dir_info.st_mode & S_IWGRP)) { #endif log_err("error: directory is writable by group or other: (%s)\n", cwd); exit(116); } /* * Error out if we cannot stat the program. */ if (((lstat(cmd, &prg_info)) != 0) || (S_ISLNK(prg_info.st_mode))) { log_err("error: cannot stat program: (%s)\n", cmd); exit(117); } /* * Error out if the program is writable by others. */ if ((prg_info.st_mode & S_IWOTH) || (prg_info.st_mode & S_IWGRP)) { log_err("error: file is writable by group or other: (%s/%s)\n", cwd, cmd); exit(118); } #ifndef TESTBED /* * Error out if the file is setuid or setgid. */ if ((prg_info.st_mode & S_ISUID) || (prg_info.st_mode & S_ISGID)) { log_err("error: file is either setuid or setgid: (%s/%s)\n", cwd, cmd); exit(119); } /* * Error out if the target name/group is different from * the name/group of the cwd or the program. */ if ((uid != dir_info.st_uid) || (gid != dir_info.st_gid) || (uid != prg_info.st_uid) || (gid != prg_info.st_gid)) { log_err("error: target uid/gid (%ld/%ld) mismatch " "with directory (%ld/%ld) or program (%ld/%ld)\n", uid, gid, dir_info.st_uid, dir_info.st_gid, prg_info.st_uid, prg_info.st_gid); exit(120); } #endif /* * Error out if the program is not executable for the user. * Otherwise, she won't find any error in the logs except for * "[error] Premature end of script headers: ..." */ if (!(prg_info.st_mode & S_IXUSR)) { log_err("error: file has no execute permission: (%s/%s)\n", cwd, cmd); exit(121); } #ifdef SUEXEC_UMASK /* * umask() uses inverse logic; bits are CLEAR for allowed access. */ if ((~SUEXEC_UMASK) & 0022) { log_err("notice: SUEXEC_UMASK of %03o allows " "write permission to group and/or other\n", SUEXEC_UMASK); } umask(SUEXEC_UMASK); #endif /* SUEXEC_UMASK */ clean_env(); /* * Be sure to close the log file so the CGI can't * mess with it. If the exec fails, it will be reopened * automatically when log_err is called. Note that the log * might not actually be open if LOG_EXEC isn't defined. * However, the "log" cell isn't ifdef'd so let's be defensive * and assume someone might have done something with it * outside an ifdef'd LOG_EXEC block. */ if (log != NULL) { fclose(log); log = NULL; } /* * Execute the command, replacing our image with its own. */ #ifdef NEED_HASHBANG_EMUL /* We need the #! emulation when we want to execute scripts */ { extern char **environ; ap_execve(cmd, &argv[3], environ); } #else /*NEED_HASHBANG_EMUL*/ execv(cmd, &argv[3]); #endif /*NEED_HASHBANG_EMUL*/ /* * (I can't help myself...sorry.) * * Uh oh. Still here. Where's the kaboom? There was supposed to be an * EARTH-shattering kaboom! * * Oh well, log the failure and error out. */ log_err("emerg: (%d)%s: exec failed (%s)\n", errno, strerror(errno), cmd); exit(255); }
int main(int argc, char *argv[]) { int userdir = 0; /* ~userdir flag */ uid_t uid; /* user information */ gid_t gid; /* target group placeholder */ char *target_uname; /* target user name */ char *target_gname; /* target group name */ char *target_homedir; /* target home directory */ char *actual_uname; /* actual user name */ char *actual_gname; /* actual group name */ char *prog; /* name of this program */ char *cmd; /* command to be executed */ char cwd[AP_MAXPATH]; /* current working directory */ char dwd[AP_MAXPATH]; /* docroot working directory */ struct passwd *pw; /* password entry holder */ struct group *gr; /* group entry holder */ struct stat dir_info; /* directory info holder */ struct stat prg_info; /* program info holder */ /* * If there are a proper number of arguments, set * all of them to variables. Otherwise, error out. */ prog = argv[0]; if (argc < 4) { log_err("too few arguments\n"); exit(101); } target_uname = argv[1]; target_gname = argv[2]; cmd = argv[3]; /* * Check existence/validity of the UID of the user * running this program. Error out if invalid. */ uid = getuid(); if ((pw = getpwuid(uid)) == NULL) { log_err("invalid uid: (%ld)\n", uid); exit(102); } /* * Check to see if the user running this program * is the user allowed to do so as defined in * suexec.h. If not the allowed user, error out. */ #ifdef _OSD_POSIX /* User name comparisons are case insensitive on BS2000/OSD */ if (strcasecmp(HTTPD_USER, pw->pw_name)) { log_err("user mismatch (%s)\n", pw->pw_name); exit(103); } #else /*_OSD_POSIX*/ if (strcmp(HTTPD_USER, pw->pw_name)) { log_err("user mismatch (%s)\n", pw->pw_name); exit(103); } #endif /*_OSD_POSIX*/ /* * Check for a leading '/' (absolute path) in the command to be executed, * or attempts to back up out of the current directory, * to protect against attacks. If any are * found, error out. Naughty naughty crackers. */ if ((cmd[0] == '/') || (!strncmp(cmd, "../", 3)) || (strstr(cmd, "/../") != NULL)) { log_err("invalid command (%s)\n", cmd); exit(104); } /* * Check to see if this is a ~userdir request. If * so, set the flag, and remove the '~' from the * target username. */ if (!strncmp("~", target_uname, 1)) { target_uname++; userdir = 1; } /* * Error out if the target username is invalid. */ if ((pw = getpwnam(target_uname)) == NULL) { log_err("invalid target user name: (%s)\n", target_uname); exit(105); } /* * Error out if the target group name is invalid. */ if (strspn(target_gname, "1234567890") != strlen(target_gname)) { if ((gr = getgrnam(target_gname)) == NULL) { log_err("invalid target group name: (%s)\n", target_gname); exit(106); } gid = gr->gr_gid; actual_gname = strdup(gr->gr_name); } else { gid = atoi(target_gname); actual_gname = strdup(target_gname); } /* * Save these for later since initgroups will hose the struct */ uid = pw->pw_uid; actual_uname = strdup(pw->pw_name); target_homedir = strdup(pw->pw_dir); /* * Log the transaction here to be sure we have an open log * before we setuid(). */ log_err("uid: (%s/%s) gid: (%s/%s) %s\n", target_uname, actual_uname, target_gname, actual_gname, cmd); /* * Error out if attempt is made to execute as root or as * a UID less than UID_MIN. Tsk tsk. */ if ((uid == 0) || (uid < UID_MIN)) { log_err("cannot run as forbidden uid (%d/%s)\n", uid, cmd); exit(107); } /* * Error out if attempt is made to execute as root group * or as a GID less than GID_MIN. Tsk tsk. */ if ((gid == 0) || (gid < GID_MIN)) { log_err("cannot run as forbidden gid (%d/%s)\n", gid, cmd); exit(108); } /* * Change UID/GID here so that the following tests work over NFS. * * Initialize the group access list for the target user, * and setgid() to the target group. If unsuccessful, error out. */ if (((setgid(gid)) != 0) || (initgroups(actual_uname, gid) != 0)) { log_err("failed to setgid (%ld: %s)\n", gid, cmd); exit(109); } /* * setuid() to the target user. Error out on fail. */ if ((setuid(uid)) != 0) { log_err("failed to setuid (%ld: %s)\n", uid, cmd); exit(110); } /* * Get the current working directory, as well as the proper * document root (dependant upon whether or not it is a * ~userdir request). Error out if we cannot get either one, * or if the current working directory is not in the docroot. * Use chdir()s and getcwd()s to avoid problems with symlinked * directories. Yuck. */ if (getcwd(cwd, AP_MAXPATH) == NULL) { log_err("cannot get current working directory\n"); exit(111); } if (userdir) { if (((chdir(target_homedir)) != 0) || ((chdir(USERDIR_SUFFIX)) != 0) || ((getcwd(dwd, AP_MAXPATH)) == NULL) || ((chdir(cwd)) != 0)) { log_err("cannot get docroot information (%s)\n", target_homedir); exit(112); } } else { if (((chdir(DOC_ROOT)) != 0) || ((getcwd(dwd, AP_MAXPATH)) == NULL) || ((chdir(cwd)) != 0)) { log_err("cannot get docroot information (%s)\n", DOC_ROOT); exit(113); } } if ((strncmp(cwd, dwd, strlen(dwd))) != 0) { log_err("command not in docroot (%s/%s)\n", cwd, cmd); exit(114); } /* * Stat the cwd and verify it is a directory, or error out. */ if (((lstat(cwd, &dir_info)) != 0) || !(S_ISDIR(dir_info.st_mode))) { log_err("cannot stat directory: (%s)\n", cwd); exit(115); } /* * Error out if cwd is writable by others. */ if ((dir_info.st_mode & S_IWOTH) || (dir_info.st_mode & S_IWGRP)) { log_err("directory is writable by others: (%s)\n", cwd); exit(116); } /* * Error out if we cannot stat the program. */ if (((lstat(cmd, &prg_info)) != 0) || (S_ISLNK(prg_info.st_mode))) { log_err("cannot stat program: (%s)\n", cmd); exit(117); } /* * Error out if the program is writable by others. */ if ((prg_info.st_mode & S_IWOTH) || (prg_info.st_mode & S_IWGRP)) { log_err("file is writable by others: (%s/%s)\n", cwd, cmd); exit(118); } /* * Error out if the file is setuid or setgid. */ if ((prg_info.st_mode & S_ISUID) || (prg_info.st_mode & S_ISGID)) { log_err("file is either setuid or setgid: (%s/%s)\n", cwd, cmd); exit(119); } /* * Error out if the target name/group is different from * the name/group of the cwd or the program. */ if ((uid != dir_info.st_uid) || (gid != dir_info.st_gid) || (uid != prg_info.st_uid) || (gid != prg_info.st_gid)) { log_err("target uid/gid (%ld/%ld) mismatch with directory (%ld/%ld) or program (%ld/%ld)\n", uid, gid, dir_info.st_uid, dir_info.st_gid, prg_info.st_uid, prg_info.st_gid); exit(120); } /* * Error out if the program is not executable for the user. * Otherwise, she won't find any error in the logs except for * "[error] Premature end of script headers: ..." */ if (!(prg_info.st_mode & S_IXUSR)) { log_err("file has no execute permission: (%s/%s)\n", cwd, cmd); exit(121); } clean_env(); /* * Be sure to close the log file so the CGI can't * mess with it. If the exec fails, it will be reopened * automatically when log_err is called. */ fclose(log); log = NULL; /* * Execute the command, replacing our image with its own. */ execv(cmd, &argv[3]); /* * (I can't help myself...sorry.) * * Uh oh. Still here. Where's the kaboom? There was supposed to be an * EARTH-shattering kaboom! * * Oh well, log the failure and error out. */ log_err("(%d)%s: exec failed (%s)\n", errno, strerror(errno), cmd); exit(255); }
int main(int argc, char **argv) { struct stat st; int fd; int c; VSTRING *buf; int status; MAIL_STREAM *dst; int rec_type; static char *segment_info[] = { REC_TYPE_POST_ENVELOPE, REC_TYPE_POST_CONTENT, REC_TYPE_POST_EXTRACT, "" }; char **expected; uid_t uid = getuid(); ARGV *import_env; const char *error_text; char *attr_name; char *attr_value; const char *errstr; char *junk; struct timeval start; int saved_errno; int from_count = 0; int rcpt_count = 0; int validate_input = 1; /* * Fingerprint executables and core dumps. */ MAIL_VERSION_STAMP_ALLOCATE; /* * Be consistent with file permissions. */ umask(022); /* * To minimize confusion, make sure that the standard file descriptors * are open before opening anything else. XXX Work around for 44BSD where * fstat can return EBADF on an open file descriptor. */ for (fd = 0; fd < 3; fd++) if (fstat(fd, &st) == -1 && (close(fd), open("/dev/null", O_RDWR, 0)) != fd) msg_fatal("open /dev/null: %m"); /* * Set up logging. Censor the process name: it is provided by the user. */ argv[0] = "postdrop"; msg_vstream_init(argv[0], VSTREAM_ERR); msg_syslog_init(mail_task("postdrop"), LOG_PID, LOG_FACILITY); set_mail_conf_str(VAR_PROCNAME, var_procname = mystrdup(argv[0])); /* * Check the Postfix library version as soon as we enable logging. */ MAIL_VERSION_CHECK; /* * Parse JCL. This program is set-gid and must sanitize all command-line * arguments. The configuration directory argument is validated by the * mail configuration read routine. Don't do complex things until we have * completed initializations. */ while ((c = GETOPT(argc, argv, "c:rv")) > 0) { switch (c) { case 'c': if (setenv(CONF_ENV_PATH, optarg, 1) < 0) msg_fatal("out of memory"); break; case 'r': /* forward compatibility */ break; case 'v': if (geteuid() == 0) msg_verbose++; break; default: msg_fatal("usage: %s [-c config_dir] [-v]", argv[0]); } } /* * Read the global configuration file and extract configuration * information. Some claim that the user should supply the working * directory instead. That might be OK, given that this command needs * write permission in a subdirectory called "maildrop". However we still * need to reliably detect incomplete input, and so we must perform * record-level I/O. With that, we should also take the opportunity to * perform some sanity checks on the input. */ mail_conf_read(); /* Re-evaluate mail_task() after reading main.cf. */ msg_syslog_init(mail_task("postdrop"), LOG_PID, LOG_FACILITY); get_mail_conf_str_table(str_table); /* * Mail submission access control. Should this be in the user-land gate, * or in the daemon process? */ mail_dict_init(); if ((errstr = check_user_acl_byuid(VAR_SUBMIT_ACL, var_submit_acl, uid)) != 0) msg_fatal("User %s(%ld) is not allowed to submit mail", errstr, (long) uid); /* * Stop run-away process accidents by limiting the queue file size. This * is not a defense against DOS attack. */ if (var_message_limit > 0 && get_file_limit() > var_message_limit) set_file_limit((off_t) var_message_limit); /* * This program is installed with setgid privileges. Strip the process * environment so that we don't have to trust the C library. */ import_env = mail_parm_split(VAR_IMPORT_ENVIRON, var_import_environ); clean_env(import_env->argv); argv_free(import_env); if (chdir(var_queue_dir)) msg_fatal("chdir %s: %m", var_queue_dir); if (msg_verbose) msg_info("chdir %s", var_queue_dir); /* * Set up signal handlers and a runtime error handler so that we can * clean up incomplete output. * * postdrop_sig() uses the in-kernel SIGINT handler address as an atomic * variable to prevent nested postdrop_sig() calls. For this reason, the * SIGINT handler must be configured before other signal handlers are * allowed to invoke postdrop_sig(). */ signal(SIGPIPE, SIG_IGN); signal(SIGXFSZ, SIG_IGN); signal(SIGINT, postdrop_sig); signal(SIGQUIT, postdrop_sig); if (signal(SIGTERM, SIG_IGN) == SIG_DFL) signal(SIGTERM, postdrop_sig); if (signal(SIGHUP, SIG_IGN) == SIG_DFL) signal(SIGHUP, postdrop_sig); msg_cleanup(postdrop_cleanup); /* End of initializations. */ /* * Don't trust the caller's time information. */ GETTIMEOFDAY(&start); /* * Create queue file. mail_stream_file() never fails. Send the queue ID * to the caller. Stash away a copy of the queue file name so we can * clean up in case of a fatal error or an interrupt. */ dst = mail_stream_file(MAIL_QUEUE_MAILDROP, MAIL_CLASS_PUBLIC, var_pickup_service, 0444); attr_print(VSTREAM_OUT, ATTR_FLAG_NONE, SEND_ATTR_STR(MAIL_ATTR_QUEUEID, dst->id), ATTR_TYPE_END); vstream_fflush(VSTREAM_OUT); postdrop_path = mystrdup(VSTREAM_PATH(dst->stream)); /* * Copy stdin to file. The format is checked so that we can recognize * incomplete input and cancel the operation. With the sanity checks * applied here, the pickup daemon could skip format checks and pass a * file descriptor to the cleanup daemon. These are by no means all * sanity checks - the cleanup service and queue manager services will * reject messages that lack required information. * * If something goes wrong, slurp up the input before responding to the * client, otherwise the client will give up after detecting SIGPIPE. * * Allow attribute records if the attribute specifies the MIME body type * (sendmail -B). */ vstream_control(VSTREAM_IN, CA_VSTREAM_CTL_PATH("stdin"), CA_VSTREAM_CTL_END); buf = vstring_alloc(100); expected = segment_info; /* Override time information from the untrusted caller. */ rec_fprintf(dst->stream, REC_TYPE_TIME, REC_TYPE_TIME_FORMAT, REC_TYPE_TIME_ARG(start)); for (;;) { /* Don't allow PTR records. */ rec_type = rec_get_raw(VSTREAM_IN, buf, var_line_limit, REC_FLAG_NONE); if (rec_type == REC_TYPE_EOF) { /* request cancelled */ mail_stream_cleanup(dst); if (remove(postdrop_path)) msg_warn("uid=%ld: remove %s: %m", (long) uid, postdrop_path); else if (msg_verbose) msg_info("remove %s", postdrop_path); myfree(postdrop_path); postdrop_path = 0; exit(0); } if (rec_type == REC_TYPE_ERROR) msg_fatal("uid=%ld: malformed input", (long) uid); if (strchr(*expected, rec_type) == 0) msg_fatal("uid=%ld: unexpected record type: %d", (long) uid, rec_type); if (rec_type == **expected) expected++; /* Override time information from the untrusted caller. */ if (rec_type == REC_TYPE_TIME) continue; /* Check these at submission time instead of pickup time. */ if (rec_type == REC_TYPE_FROM) from_count++; if (rec_type == REC_TYPE_RCPT) rcpt_count++; /* Limit the attribute types that users may specify. */ if (rec_type == REC_TYPE_ATTR) { if ((error_text = split_nameval(vstring_str(buf), &attr_name, &attr_value)) != 0) { msg_warn("uid=%ld: ignoring malformed record: %s: %.200s", (long) uid, error_text, vstring_str(buf)); continue; } #define STREQ(x,y) (strcmp(x,y) == 0) if ((STREQ(attr_name, MAIL_ATTR_ENCODING) && (STREQ(attr_value, MAIL_ATTR_ENC_7BIT) || STREQ(attr_value, MAIL_ATTR_ENC_8BIT) || STREQ(attr_value, MAIL_ATTR_ENC_NONE))) || STREQ(attr_name, MAIL_ATTR_DSN_ENVID) || STREQ(attr_name, MAIL_ATTR_DSN_NOTIFY) || rec_attr_map(attr_name) || (STREQ(attr_name, MAIL_ATTR_RWR_CONTEXT) && (STREQ(attr_value, MAIL_ATTR_RWR_LOCAL) || STREQ(attr_value, MAIL_ATTR_RWR_REMOTE))) || STREQ(attr_name, MAIL_ATTR_TRACE_FLAGS)) { /* XXX */ rec_fprintf(dst->stream, REC_TYPE_ATTR, "%s=%s", attr_name, attr_value); } else { msg_warn("uid=%ld: ignoring attribute record: %.200s=%.200s", (long) uid, attr_name, attr_value); } continue; } if (REC_PUT_BUF(dst->stream, rec_type, buf) < 0) { /* rec_get() errors must not clobber errno. */ saved_errno = errno; while ((rec_type = rec_get_raw(VSTREAM_IN, buf, var_line_limit, REC_FLAG_NONE)) != REC_TYPE_END && rec_type != REC_TYPE_EOF) if (rec_type == REC_TYPE_ERROR) msg_fatal("uid=%ld: malformed input", (long) uid); validate_input = 0; errno = saved_errno; break; } if (rec_type == REC_TYPE_END) break; } vstring_free(buf); /* * As of Postfix 2.7 the pickup daemon discards mail without recipients. * Such mail may enter the maildrop queue when "postsuper -r" is invoked * before the queue manager deletes an already delivered message. Looking * at file ownership is not a good way to make decisions on what mail to * discard. Instead, the pickup server now requires that new submissions * always have at least one recipient record. * * The Postfix sendmail command already rejects mail without recipients. * However, in the future postdrop may receive mail via other programs, * so we add a redundant recipient check here for future proofing. * * The test for the sender address is just for consistency of error * reporting (report at submission time instead of pickup time). Besides * the segment terminator records, there aren't any other mandatory * records in a Postfix submission queue file. */ if (validate_input && (from_count == 0 || rcpt_count == 0)) { status = CLEANUP_STAT_BAD; mail_stream_cleanup(dst); } /* * Finish the file. */ else if ((status = mail_stream_finish(dst, (VSTRING *) 0)) != 0) { msg_warn("uid=%ld: %m", (long) uid); postdrop_cleanup(); } /* * Disable deletion on fatal error before reporting success, so the file * will not be deleted after we have taken responsibility for delivery. */ if (postdrop_path) { junk = postdrop_path; postdrop_path = 0; myfree(junk); } /* * Send the completion status to the caller and terminate. */ attr_print(VSTREAM_OUT, ATTR_FLAG_NONE, SEND_ATTR_INT(MAIL_ATTR_STATUS, status), SEND_ATTR_STR(MAIL_ATTR_WHY, ""), ATTR_TYPE_END); vstream_fflush(VSTREAM_OUT); exit(status); }
int main(int argc, char **argv) { char *script; struct stat st; char *slash; int fd; int ch; ARGV *import_env; static const CONFIG_STR_TABLE str_table[] = { VAR_SENDMAIL_PATH, DEF_SENDMAIL_PATH, &var_sendmail_path, 1, 0, VAR_MAILQ_PATH, DEF_MAILQ_PATH, &var_mailq_path, 1, 0, VAR_NEWALIAS_PATH, DEF_NEWALIAS_PATH, &var_newalias_path, 1, 0, VAR_MANPAGE_DIR, DEF_MANPAGE_DIR, &var_manpage_dir, 1, 0, VAR_SAMPLE_DIR, DEF_SAMPLE_DIR, &var_sample_dir, 1, 0, VAR_README_DIR, DEF_README_DIR, &var_readme_dir, 1, 0, VAR_HTML_DIR, DEF_HTML_DIR, &var_html_dir, 1, 0, 0, }; int force_single_instance; ARGV *my_argv; /* * Fingerprint executables and core dumps. */ MAIL_VERSION_STAMP_ALLOCATE; /* * Be consistent with file permissions. */ umask(022); /* * To minimize confusion, make sure that the standard file descriptors * are open before opening anything else. XXX Work around for 44BSD where * fstat can return EBADF on an open file descriptor. */ for (fd = 0; fd < 3; fd++) if (fstat(fd, &st) == -1 && (close(fd), open("/dev/null", O_RDWR, 0)) != fd) msg_fatal("open /dev/null: %m"); /* * Set up diagnostics. XXX What if stdin is the system console during * boot time? It seems a bad idea to log startup errors to the console. * This is UNIX, a system that can run without hand holding. */ if ((slash = strrchr(argv[0], '/')) != 0 && slash[1]) argv[0] = slash + 1; if (isatty(STDERR_FILENO)) msg_vstream_init(argv[0], VSTREAM_ERR); msg_syslog_init(argv[0], LOG_PID, LOG_FACILITY); /* * Check the Postfix library version as soon as we enable logging. */ MAIL_VERSION_CHECK; /* * The mail system must be run by the superuser so it can revoke * privileges for selected operations. That's right - it takes privileges * to toss privileges. */ if (getuid() != 0) { msg_error("to submit mail, use the Postfix sendmail command"); msg_fatal("the postfix command is reserved for the superuser"); } if (unsafe() != 0) msg_fatal("the postfix command must not run as a set-uid process"); /* * Parse switches. */ while ((ch = GETOPT(argc, argv, "c:Dv")) > 0) { switch (ch) { default: msg_fatal("usage: %s [-c config_dir] [-Dv] command", argv[0]); case 'c': if (*optarg != '/') msg_fatal("-c requires absolute pathname"); check_setenv(CONF_ENV_PATH, optarg); break; case 'D': check_setenv(CONF_ENV_DEBUG, ""); break; case 'v': msg_verbose++; check_setenv(CONF_ENV_VERB, ""); break; } } force_single_instance = (getenv(CONF_ENV_PATH) != 0); /* * Copy a bunch of configuration parameters into the environment for easy * access by the maintenance shell script. */ mail_conf_read(); get_mail_conf_str_table(str_table); /* * Environment import filter, to enforce consistent behavior whether this * command is started by hand, or at system boot time. This is necessary * because some shell scripts use environment settings to override * main.cf settings. */ import_env = argv_split(var_import_environ, ", \t\r\n"); clean_env(import_env->argv); argv_free(import_env); check_setenv("PATH", ROOT_PATH); /* sys_defs.h */ check_setenv(CONF_ENV_PATH, var_config_dir);/* mail_conf.h */ check_setenv(VAR_COMMAND_DIR, var_command_dir); /* main.cf */ check_setenv(VAR_DAEMON_DIR, var_daemon_dir); /* main.cf */ check_setenv(VAR_DATA_DIR, var_data_dir); /* main.cf */ check_setenv(VAR_QUEUE_DIR, var_queue_dir); /* main.cf */ check_setenv(VAR_CONFIG_DIR, var_config_dir); /* main.cf */ /* * Do we want to keep adding things here as shell scripts evolve? */ check_setenv(VAR_MAIL_OWNER, var_mail_owner); /* main.cf */ check_setenv(VAR_SGID_GROUP, var_sgid_group); /* main.cf */ check_setenv(VAR_SENDMAIL_PATH, var_sendmail_path); /* main.cf */ check_setenv(VAR_MAILQ_PATH, var_mailq_path); /* main.cf */ check_setenv(VAR_NEWALIAS_PATH, var_newalias_path); /* main.cf */ check_setenv(VAR_MANPAGE_DIR, var_manpage_dir); /* main.cf */ check_setenv(VAR_SAMPLE_DIR, var_sample_dir); /* main.cf */ check_setenv(VAR_README_DIR, var_readme_dir); /* main.cf */ check_setenv(VAR_HTML_DIR, var_html_dir); /* main.cf */ /* * Make sure these directories exist. Run the maintenance scripts with as * current directory the mail database. */ if (chdir(var_command_dir)) msg_fatal("chdir(%s): %m", var_command_dir); if (chdir(var_daemon_dir)) msg_fatal("chdir(%s): %m", var_daemon_dir); if (chdir(var_queue_dir)) msg_fatal("chdir(%s): %m", var_queue_dir); /* * Run the management script. */ if (force_single_instance || argv_split(var_multi_conf_dirs, "\t\r\n, ")->argc == 0) { script = concatenate(var_daemon_dir, "/postfix-script", (char *) 0); if (optind < 1) msg_panic("bad optind value"); argv[optind - 1] = script; execvp(script, argv + optind - 1); msg_fatal("%s: %m", script); } /* * Hand off control to a multi-instance manager. */ else { if (*var_multi_wrapper == 0) msg_fatal("multi-instance support is requested, but %s is empty", VAR_MULTI_WRAPPER); my_argv = argv_split(var_multi_wrapper, " \t\r\n"); do { argv_add(my_argv, argv[optind], (char *) 0); } while (argv[optind++] != 0); execvp(my_argv->argv[0], my_argv->argv); msg_fatal("%s: %m", my_argv->argv[0]); } }