Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  postdrop.c

  Sprache: C
 

/*++
/* NAME
/* postdrop 1
/* SUMMARY
/* Postfix mail posting utility
/* SYNOPSIS
/* \fBpostdrop\fR [\fB-rv\fR] [\fB-c \fIconfig_dir\fR]
/* DESCRIPTION
/* The \fBpostdrop\fR(1) command creates a file in the \fBmaildrop\fR
/* directory and copies its standard input to the file.
/*
/* Options:
/* .IP "\fB-c \fIconfig_dir\fR"
/* The \fBmain.cf\fR configuration file is in the named directory
/* instead of the default configuration directory. See also the
/* MAIL_CONFIG environment setting below.
/* .IP \fB-r\fR
/* Use a Postfix-internal protocol for reading the message from
/* standard input, and for reporting status information on standard
/* output. This is currently the only supported method.
/* .IP \fB-v\fR
/* Enable verbose logging for debugging purposes. Multiple \fB-v\fR
/* options make the software increasingly verbose. As of Postfix 2.3,
/* this option is available for the super-user only.
/* SECURITY
/* .ad
/* .fi
/* The command is designed to run with set-group ID privileges, so
/* that it can write to the \fBmaildrop\fR queue directory and so that
/* it can connect to Postfix daemon processes.
/* DIAGNOSTICS
/* Fatal errors: malformed input, I/O error, out of memory. Problems
/* are logged to \fBsyslogd\fR(8) or \fBpostlogd\fR(8) and to
/* the standard error stream.
/* When the input is incomplete, or when the process receives a HUP,
/* INT, QUIT or TERM signal, the queue file is deleted.
/* ENVIRONMENT
/* .ad
/* .fi
/* .IP MAIL_CONFIG
/* Directory with the \fBmain.cf\fR file. In order to avoid exploitation
/* of set-group ID privileges, a non-default directory is allowed only
/* if:
/* .RS
/* .IP \(bu
/* The name is listed in the default \fBmain.cf\fR file with the
/* \fBalternate_config_directories\fR or
/* \fBmulti_instance_directories\fR configuration parameter.
/* .IP \(bu
/* The command is invoked by the super-user.
/* .RE
/* CONFIGURATION PARAMETERS
/* .ad
/* .fi
/* The following \fBmain.cf\fR parameters are especially relevant to
/* this program.
/* The text below provides only a parameter summary. See
/* \fBpostconf\fR(5) for more details including examples.
/* .IP "\fBalternate_config_directories (empty)\fR"
/* A list of non-default Postfix configuration directories that may
/* be specified with "-c config_directory" on the command line (in the
/* case of \fBsendmail\fR(1), with the "-C" option), or via the MAIL_CONFIG
/* environment parameter.
/* .IP "\fBconfig_directory (see 'postconf -d' output)\fR"
/* The default location of the Postfix main.cf and master.cf
/* configuration files.
/* .IP "\fBimport_environment (see 'postconf -d' output)\fR"
/* The list of environment variables that a privileged Postfix
/* process will import from a non-Postfix parent process, or name=value
/* environment overrides.
/* .IP "\fBqueue_directory (see 'postconf -d' output)\fR"
/* The location of the Postfix top-level queue directory.
/* .IP "\fBsyslog_facility (mail)\fR"
/* The syslog facility of Postfix logging.
/* .IP "\fBsyslog_name (see 'postconf -d' output)\fR"
/* A prefix that is prepended to the process name in syslog
/* records, so that, for example, "smtpd" becomes "prefix/smtpd".
/* .IP "\fBtrigger_timeout (10s)\fR"
/* The time limit for sending a trigger to a Postfix daemon (for
/* example, the \fBpickup\fR(8) or \fBqmgr\fR(8) daemon).
/* .PP
/* Available in Postfix version 2.2 and later:
/* .IP "\fBauthorized_submit_users (static:anyone)\fR"
/* List of users who are authorized to submit mail with the \fBsendmail\fR(1)
/* command (and with the privileged \fBpostdrop\fR(1) helper command).
/* .PP
/* Available in Postfix version 3.6 and later:
/* .IP "\fBlocal_login_sender_maps (static:*)\fR"
/* A list of lookup tables that are searched by the UNIX login name,
/* and that return a list of allowed envelope sender patterns separated
/* by space or comma.
/* .IP "\fBempty_address_local_login_sender_maps_lookup_key (<>)\fR"
/* The lookup key to be used in local_login_sender_maps tables, instead
/* of the null sender address.
/* .IP "\fBrecipient_delimiter (empty)\fR"
/* The set of characters that can separate an email address
/* localpart, user name, or a .forward file name from its extension.
/* FILES
/* /var/spool/postfix/maildrop, maildrop queue
/* SEE ALSO
/* sendmail(1), compatibility interface
/* postconf(5), configuration parameters
/* postlogd(8), Postfix logging
/* syslogd(8), system logging
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*
/* Wietse Venema
/* Google, Inc.
/* 111 8th Avenue
/* New York, NY 10011, USA
/*--*/


/* System library. */

#include <sys_defs.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>   /* remove() */
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <errno.h>
#include <warn_stat.h>

/* Utility library. */

#include <msg.h>
#include <mymalloc.h>
#include <vstream.h>
#include <vstring.h>
#include <msg_vstream.h>
#include <argv.h>
#include <iostuff.h>
#include <stringops.h>
#include <mypwd.h>

/* Global library. */

#include <mail_proto.h>
#include <mail_queue.h>
#include <mail_params.h>
#include <mail_version.h>
#include <mail_conf.h>
#include <mail_task.h>
#include <clean_env.h>
#include <mail_stream.h>
#include <cleanup_user.h>
#include <record.h>
#include <rec_type.h>
#include <mail_dict.h>
#include <user_acl.h>
#include <rec_attr_map.h>
#include <mail_parm_split.h>
#include <maillog_client.h>
#include <login_sender_match.h>

/* Application-specific. */

 /*
  * WARNING WARNING WARNING
  * 
  * This software is designed to run set-gid. In order to avoid exploitation of
  * privilege, this software should not run any external commands, nor should
  * it take any information from the user unless that information can be
  * properly sanitized. To get an idea of how much information a process can
  * inherit from a potentially hostile user, examine all the members of the
  * process structure (typically, in /usr/include/sys/proc.h): the current
  * directory, open files, timers, signals, environment, command line, umask,
  * and so on.
  */


 /*
  * Local mail submission access list.
  */

char   *var_submit_acl;
char   *var_local_login_snd_maps;
char   *var_null_local_login_snd_maps_key;

static const CONFIG_STR_TABLE str_table[] = {
    VAR_SUBMIT_ACL, DEF_SUBMIT_ACL, &var_submit_acl, 00,
    VAR_LOCAL_LOGIN_SND_MAPS, DEF_LOCAL_LOGIN_SND_MAPS, &var_local_login_snd_maps, 00,
    VAR_NULL_LOCAL_LOGIN_SND_MAPS_KEY, DEF_NULL_LOCAL_LOGIN_SND_MAPS_KEY, &var_null_local_login_snd_maps_key, 00,
    0,
};

 /*
  * Queue file name. Global, so that the cleanup routine can find it when
  * called by the run-time error handler.
  */

static char *postdrop_path;

/* postdrop_sig - catch signal and clean up */

static void postdrop_sig(int sig)
{

    /*
     * This is the fatal error handler. Don't try to do anything fancy.
     * 
     * To avoid privilege escalation in a set-gid program, Postfix logging
     * functions must not be called from a user-triggered signal handler,
     * because Postfix logging functions may allocate memory on the fly (as
     * does the syslog() library function), and the memory allocator is not
     * reentrant.
     * 
     * Assume atomic signal() updates, even when emulated with sigaction(). We
     * use the in-kernel SIGINT handler address as an atomic variable to
     * prevent nested postdrop_sig() calls. For this reason, main() must
     * configure postdrop_sig() as SIGINT handler before other signal
     * handlers are allowed to invoke postdrop_sig().
     */

    if (signal(SIGINT, SIG_IGN) != SIG_IGN) {
 (void) signal(SIGQUIT, SIG_IGN);
 (void) signal(SIGTERM, SIG_IGN);
 (void) signal(SIGHUP, SIG_IGN);
 if (postdrop_path) {
     (void) remove(postdrop_path);
     postdrop_path = 0;
 }
 /* Future proofing. If you need exit() here then you broke Postfix. */
 if (sig)
     _exit(sig);
    }
}

/* postdrop_cleanup - callback for the runtime error handler */

static void postdrop_cleanup(void)
{
    postdrop_sig(0);
}

/* check_login_sender_acl - check if a user is authorized to use this sender */

static int check_login_sender_acl(uid_t uid, VSTRING *sender_buf,
              VSTRING *reason)
{
    const char myname[] = "check_login_sender_acl";
    struct mypasswd *user_info;
    char   *user_name;
    VSTRING *user_name_buf = 0;
    LOGIN_SENDER_MATCH *lsm;
    int     res;

    /*
     * Sanity checks.
     */

    if (vstring_memchr(sender_buf, '\0') != 0) {
 vstring_sprintf(reason, "NUL in FROM record");
 return (CLEANUP_STAT_BAD);
    }

    /*
     * Optimization.
     */

#ifndef SNAPSHOT
    if (strcmp(var_local_login_snd_maps, DEF_LOCAL_LOGIN_SND_MAPS) == 0)
 return (CLEANUP_STAT_OK);
#endif

    /*
     * Get the username.
     */

    if ((user_info = mypwuid(uid)) != 0) {
 user_name = user_info->pw_name;
    } else {
 user_name_buf = vstring_alloc(10);
 vstring_sprintf(user_name_buf, "uid:%ld", (long) uid);
 user_name = vstring_str(user_name_buf);
    }


    /*
     * Apply the a login-sender matcher. TODO: add DICT flags.
     */

    lsm = login_sender_create(VAR_LOCAL_LOGIN_SND_MAPS,
         var_local_login_snd_maps,
         var_rcpt_delim,
         var_null_local_login_snd_maps_key, "*");
    res = login_sender_match(lsm, user_name, vstring_str(sender_buf));
    login_sender_free(lsm);
    if (user_name_buf)
 vstring_free(user_name_buf);
    switch (res) {
    case LSM_STAT_FOUND:
 return (CLEANUP_STAT_OK);
    case LSM_STAT_NOTFOUND:
 vstring_sprintf(reason, "not authorized to use sender='%s'",
   vstring_str(sender_buf));
 return (CLEANUP_STAT_NOPERM);
    case LSM_STAT_RETRY:
    case LSM_STAT_CONFIG:
 vstring_sprintf(reason, "%s table lookup error for '%s'",
   VAR_LOCAL_LOGIN_SND_MAPS, var_local_login_snd_maps);
 return (CLEANUP_STAT_WRITE);
    default:
 msg_panic("%s: bad login_sender_match() result: %d", myname, res);
    }
}

MAIL_VERSION_STAMP_DECLARE;

/* main - the main program */

int     main(int argc, char **argv)
{
    struct stat st;
    int     fd;
    int     c;
    VSTRING *buf;
    int     status = CLEANUP_STAT_OK;
    VSTRING *reason = vstring_alloc(100);
    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);
    maillog_client_init(mail_task("postdrop"), MAILLOG_CLIENT_FLAG_NONE);
    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.
     */

    mail_conf_read();
    /* Re-evaluate mail_task() after reading main.cf. */
    maillog_client_init(mail_task("postdrop"), MAILLOG_CLIENT_FLAG_NONE);
    get_mail_conf_str_table(str_table);

    /*
     * Stop run-away process accidents by limiting the queue file size. This
     * is not a defense against DOS attack.
     */

    if (ENFORCING_SIZE_LIMIT(var_message_limit)
 && 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. */

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

    /*
     * 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_PROTO, MAIL_ATTR_PROTO_POSTDROP),
        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 canceled */
     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) {
     status |= check_login_sender_acl(uid, buf, reason);
     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 (status != CLEANUP_STAT_OK
     || 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.
     * 
     * TODO: return an informative reason for missing sender, too many senders,
     * or missing recipient.
     */

    if (validate_input && (from_count == 0 || rcpt_count == 0))
 status |= CLEANUP_STAT_BAD;
    if (status != CLEANUP_STAT_OK) {
 mail_stream_cleanup(dst);
    }

    /*
     * Finish the file.
     */

    else if ((status = mail_stream_finish(dst, reason)) != 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, status != CLEANUP_STAT_OK
        && VSTRING_LEN(reason) > 0 ?
        vstring_str(reason) : ""),
        ATTR_TYPE_END);
    vstream_fflush(VSTREAM_OUT);
    exit(status);
}

Messung V0.5 in Prozent
C=85 H=97 G=91

¤ Dauer der Verarbeitung: 0.17 Sekunden  (vorverarbeitet am  2026-08-08) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=277311
#Domains=752002