~ubuntu-branches/ubuntu/hoary/postfix/hoary-security

« back to all changes in this revision

Viewing changes to src/showq/showq.c

  • Committer: Bazaar Package Importer
  • Author(s): LaMont Jones
  • Date: 2004-10-06 11:50:33 UTC
  • Revision ID: james.westby@ubuntu.com-20041006115033-ooo6yfg6kmoteu04
Tags: upstream-2.1.3
ImportĀ upstreamĀ versionĀ 2.1.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*++
 
2
/* NAME
 
3
/*      showq 8
 
4
/* SUMMARY
 
5
/*      list the Postfix mail queue
 
6
/* SYNOPSIS
 
7
/*      \fBshowq\fR [generic Postfix daemon options]
 
8
/* DESCRIPTION
 
9
/*      The \fBshowq\fR daemon reports the Postfix mail queue status.
 
10
/*      It is the program that emulates the sendmail `mailq' command.
 
11
/*
 
12
/*      The \fBshowq\fR daemon can also be run in stand-alone mode
 
13
/*      by the superuser. This mode of operation is used to emulate
 
14
/*      the `mailq' command while the Postfix mail system is down.
 
15
/* SECURITY
 
16
/* .ad
 
17
/* .fi
 
18
/*      The \fBshowq\fR daemon can run in a chroot jail at fixed low
 
19
/*      privilege, and takes no input from the client. Its service port
 
20
/*      is accessible to local untrusted users, so the service can be
 
21
/*      susceptible to denial of service attacks.
 
22
/* STANDARDS
 
23
/* .ad
 
24
/* .fi
 
25
/*      None. The showq daemon does not interact with the outside world.
 
26
/* DIAGNOSTICS
 
27
/*      Problems and transactions are logged to \fBsyslogd\fR(8).
 
28
/* BUGS
 
29
/*      The \fBshowq\fR daemon runs at a fixed low privilege; consequently,
 
30
/*      it cannot extract information from queue files in the
 
31
/*      \fBmaildrop\fR directory.
 
32
/* CONFIGURATION PARAMETERS
 
33
/* .ad
 
34
/* .fi
 
35
/*      Changes to \fBmain.cf\fR are picked up automatically as showq(8)
 
36
/*      processes run for only a limited amount of time. Use the command
 
37
/*      "\fBpostfix reload\fR" to speed up a change.
 
38
/*
 
39
/*      The text below provides only a parameter summary. See
 
40
/*      postconf(5) for more details including examples.
 
41
/* .IP "\fBconfig_directory (see 'postconf -d' output)\fR"
 
42
/*      The default location of the Postfix main.cf and master.cf
 
43
/*      configuration files.
 
44
/* .IP "\fBdaemon_timeout (18000s)\fR"
 
45
/*      How much time a Postfix daemon process may take to handle a
 
46
/*      request before it is terminated by a built-in watchdog timer.
 
47
/* .IP "\fBduplicate_filter_limit (1000)\fR"
 
48
/*      The maximal number of addresses remembered by the address
 
49
/*      duplicate filter for aliases(5) or virtual(5) alias expansion, or
 
50
/*      for showq(8) queue displays.
 
51
/* .IP "\fBempty_address_recipient (MAILER-DAEMON)\fR"
 
52
/*      The recipient of mail addressed to the null address.
 
53
/* .IP "\fBipc_timeout (3600s)\fR"
 
54
/*      The time limit for sending or receiving information over an internal
 
55
/*      communication channel.
 
56
/* .IP "\fBmax_idle (100s)\fR"
 
57
/*      The maximum amount of time that an idle Postfix daemon process
 
58
/*      waits for the next service request before exiting.
 
59
/* .IP "\fBmax_use (100)\fR"
 
60
/*      The maximal number of connection requests before a Postfix daemon
 
61
/*      process terminates.
 
62
/* .IP "\fBprocess_id (read-only)\fR"
 
63
/*      The process ID of a Postfix command or daemon process.
 
64
/* .IP "\fBprocess_name (read-only)\fR"
 
65
/*      The process name of a Postfix command or daemon process.
 
66
/* .IP "\fBqueue_directory (see 'postconf -d' output)\fR"
 
67
/*      The location of the Postfix top-level queue directory.
 
68
/* .IP "\fBsyslog_facility (mail)\fR"
 
69
/*      The syslog facility of Postfix logging.
 
70
/* .IP "\fBsyslog_name (postfix)\fR"
 
71
/*      The mail system name that is prepended to the process name in syslog
 
72
/*      records, so that "smtpd" becomes, for example, "postfix/smtpd".
 
73
/* FILES
 
74
/*      /var/spool/postfix, queue directories
 
75
/* SEE ALSO
 
76
/*      pickup(8), local mail pickup service
 
77
/*      cleanup(8), canonicalize and enqueue mail
 
78
/*      qmgr(8), queue manager
 
79
/*      postconf(5), configuration parameters
 
80
/*      master(8), process manager
 
81
/*      syslogd(8), system logging
 
82
/* LICENSE
 
83
/* .ad
 
84
/* .fi
 
85
/*      The Secure Mailer license must be distributed with this software.
 
86
/* AUTHOR(S)
 
87
/*      Wietse Venema
 
88
/*      IBM T.J. Watson Research
 
89
/*      P.O. Box 704
 
90
/*      Yorktown Heights, NY 10598, USA
 
91
/*--*/
 
92
 
 
93
/* System library. */
 
94
 
 
95
#include <sys_defs.h>
 
96
#include <sys/stat.h>
 
97
#include <dirent.h>
 
98
#include <stdlib.h>
 
99
#include <unistd.h>
 
100
#include <errno.h>
 
101
#include <fcntl.h>
 
102
#include <time.h>
 
103
#include <string.h>
 
104
#include <ctype.h>
 
105
 
 
106
/* Utility library. */
 
107
 
 
108
#include <msg.h>
 
109
#include <scan_dir.h>
 
110
#include <vstring.h>
 
111
#include <vstream.h>
 
112
#include <vstring_vstream.h>
 
113
#include <stringops.h>
 
114
#include <mymalloc.h>
 
115
#include <htable.h>
 
116
 
 
117
/* Global library. */
 
118
 
 
119
#include <mail_queue.h>
 
120
#include <mail_open_ok.h>
 
121
#include <mail_proto.h>
 
122
#include <mail_date.h>
 
123
#include <mail_params.h>
 
124
#include <mail_scan_dir.h>
 
125
#include <mail_conf.h>
 
126
#include <record.h>
 
127
#include <rec_type.h>
 
128
#include <quote_822_local.h>
 
129
#include <mail_addr.h>
 
130
#include <bounce_log.h>
 
131
 
 
132
/* Single-threaded server skeleton. */
 
133
 
 
134
#include <mail_server.h>
 
135
 
 
136
/* Application-specific. */
 
137
 
 
138
int     var_dup_filter_limit;
 
139
char   *var_empty_addr;
 
140
 
 
141
#define STRING_FORMAT   "%-10s %8s %-20s %s\n"
 
142
#define SENDER_FORMAT   "%-11s%8ld %20.20s %s\n"
 
143
#define DROP_FORMAT     "%-10s%c%8ld %20.20s (maildrop queue, sender UID %u)\n"
 
144
 
 
145
static void showq_reasons(VSTREAM *, BOUNCE_LOG *, HTABLE *);
 
146
 
 
147
#define STR(x)  vstring_str(x)
 
148
 
 
149
/* showq_report - report status of sender and recipients */
 
150
 
 
151
static void showq_report(VSTREAM *client, char *queue, char *id,
 
152
                                 VSTREAM *qfile, long size, time_t mtime)
 
153
{
 
154
    VSTRING *buf = vstring_alloc(100);
 
155
    VSTRING *printable_quoted_addr = vstring_alloc(100);
 
156
    int     rec_type;
 
157
    time_t  arrival_time = 0;
 
158
    char   *start;
 
159
    long    msg_size = 0;
 
160
    BOUNCE_LOG *logfile;
 
161
    HTABLE *dup_filter = 0;
 
162
    char    status = (strcmp(queue, MAIL_QUEUE_ACTIVE) == 0 ? '*' :
 
163
                      strcmp(queue, MAIL_QUEUE_HOLD) == 0 ? '!' : ' ');
 
164
    int     msg_size_ok = 0;
 
165
 
 
166
    /*
 
167
     * XXX addresses in defer logfiles are in printable quoted form, while
 
168
     * addresses in message envelope records are in raw unquoted form. This
 
169
     * may change once we replace the present ad-hoc bounce/defer logfile
 
170
     * format by one that is transparent for control etc. characters. See
 
171
     * also: bounce/bounce_append_service.c.
 
172
     * 
 
173
     * XXX With Postfix <= 2.0, "postsuper -r" results in obsolete size records
 
174
     * from previous cleanup runs. Skip the obsolete size records.
 
175
     */
 
176
    while (!vstream_ferror(client) && (rec_type = rec_get(qfile, buf, 0)) > 0) {
 
177
        start = vstring_str(buf);
 
178
        if (msg_verbose)
 
179
            msg_info("record %c %s", rec_type, printable(start, '?'));
 
180
        switch (rec_type) {
 
181
        case REC_TYPE_TIME:
 
182
            arrival_time = atol(start);
 
183
            break;
 
184
        case REC_TYPE_SIZE:
 
185
            if (msg_size == 0) {
 
186
                if ((msg_size_ok = ((msg_size = atol(start)) > 0)) == 0) {
 
187
                    msg_warn("%s: malformed size record: %.100s",
 
188
                             id, printable(start, '?'));
 
189
                    msg_size = size;
 
190
                }
 
191
            }
 
192
            break;
 
193
        case REC_TYPE_FROM:
 
194
            if (*start == 0)
 
195
                start = var_empty_addr;
 
196
            quote_822_local(printable_quoted_addr, start);
 
197
            printable(STR(printable_quoted_addr), '?');
 
198
            /* quote_822_local() saves buf, so we can reuse its space. */
 
199
            vstring_sprintf(buf, "%s%c", id, status);
 
200
            vstream_fprintf(client, SENDER_FORMAT, STR(buf),
 
201
                          msg_size > 0 ? msg_size : size, arrival_time > 0 ?
 
202
                            asctime(localtime(&arrival_time)) :
 
203
                            asctime(localtime(&mtime)),
 
204
                            STR(printable_quoted_addr));
 
205
            break;
 
206
        case REC_TYPE_RCPT:
 
207
            if (*start == 0)                    /* can't happen? */
 
208
                start = var_empty_addr;
 
209
            quote_822_local(printable_quoted_addr, start);
 
210
            printable(STR(printable_quoted_addr), '?');
 
211
            if (dup_filter == 0
 
212
              || htable_locate(dup_filter, STR(printable_quoted_addr)) == 0)
 
213
                vstream_fprintf(client, STRING_FORMAT,
 
214
                                "", "", "", STR(printable_quoted_addr));
 
215
            break;
 
216
        case REC_TYPE_MESG:
 
217
            if (msg_size_ok && vstream_fseek(qfile, msg_size, SEEK_CUR) < 0)
 
218
                msg_fatal("seek file %s: %m", VSTREAM_PATH(qfile));
 
219
            break;
 
220
        case REC_TYPE_END:
 
221
            break;
 
222
        }
 
223
 
 
224
        /*
 
225
         * With the heading printed, see if there is a defer logfile. The
 
226
         * defer logfile is not necessarily complete: delivery may be
 
227
         * interrupted (postfix stop or reload) before all recipients have
 
228
         * been tried.
 
229
         * 
 
230
         * Therefore we keep a record of recipients found in the defer logfile,
 
231
         * and try to avoid listing those recipients again when processing
 
232
         * the remainder of the queue file.
 
233
         */
 
234
        if (rec_type == REC_TYPE_FROM
 
235
            && dup_filter == 0
 
236
            && (logfile = bounce_log_open(MAIL_QUEUE_DEFER, id, O_RDONLY, 0)) != 0) {
 
237
            dup_filter = htable_create(var_dup_filter_limit);
 
238
            showq_reasons(client, logfile, dup_filter);
 
239
            if (bounce_log_close(logfile))
 
240
                msg_warn("close %s %s: %m", MAIL_QUEUE_DEFER, id);
 
241
        }
 
242
    }
 
243
    vstring_free(buf);
 
244
    vstring_free(printable_quoted_addr);
 
245
    if (dup_filter)
 
246
        htable_free(dup_filter, (void (*) (char *)) 0);
 
247
}
 
248
 
 
249
/* showq_reasons - show deferral reasons */
 
250
 
 
251
static void showq_reasons(VSTREAM *client, BOUNCE_LOG *bp, HTABLE *dup_filter)
 
252
{
 
253
    char   *saved_reason = 0;
 
254
    int     padding;
 
255
 
 
256
    while (bounce_log_read(bp) != 0) {
 
257
 
 
258
        /*
 
259
         * Update the duplicate filter.
 
260
         */
 
261
        if (var_dup_filter_limit == 0
 
262
            || dup_filter->used < var_dup_filter_limit)
 
263
            if (htable_locate(dup_filter, bp->recipient) == 0)
 
264
                htable_enter(dup_filter, bp->recipient, (char *) 0);
 
265
 
 
266
        /*
 
267
         * Don't print the reason when the previous recipient had the same
 
268
         * problem.
 
269
         */
 
270
        if (saved_reason == 0 || strcmp(saved_reason, bp->text) != 0) {
 
271
            if (saved_reason)
 
272
                myfree(saved_reason);
 
273
            saved_reason = mystrdup(bp->text);
 
274
            if ((padding = 76 - strlen(saved_reason)) < 0)
 
275
                padding = 0;
 
276
            vstream_fprintf(client, "%*s(%s)\n", padding, "", saved_reason);
 
277
        }
 
278
        vstream_fprintf(client, STRING_FORMAT, "", "", "", bp->recipient);
 
279
    }
 
280
    if (saved_reason)
 
281
        myfree(saved_reason);
 
282
}
 
283
 
 
284
 
 
285
/* showq_service - service client */
 
286
 
 
287
static void showq_service(VSTREAM *client, char *unused_service, char **argv)
 
288
{
 
289
    VSTREAM *qfile;
 
290
    const char *path;
 
291
    int     status;
 
292
    char   *id;
 
293
    int     file_count;
 
294
    unsigned long queue_size = 0;
 
295
    struct stat st;
 
296
    struct queue_info {
 
297
        char   *name;                   /* queue name */
 
298
        char   *(*scan_next) (SCAN_DIR *);      /* flat or recursive */
 
299
    };
 
300
    struct queue_info *qp;
 
301
 
 
302
    static struct queue_info queue_info[] = {
 
303
        MAIL_QUEUE_MAILDROP, scan_dir_next,
 
304
        MAIL_QUEUE_ACTIVE, mail_scan_dir_next,
 
305
        MAIL_QUEUE_INCOMING, mail_scan_dir_next,
 
306
        MAIL_QUEUE_DEFERRED, mail_scan_dir_next,
 
307
        MAIL_QUEUE_HOLD, mail_scan_dir_next,
 
308
        0,
 
309
    };
 
310
 
 
311
    /*
 
312
     * Sanity check. This service takes no command-line arguments.
 
313
     */
 
314
    if (argv[0])
 
315
        msg_fatal("unexpected command-line argument: %s", argv[0]);
 
316
 
 
317
    /*
 
318
     * Skip any files that have the wrong permissions. If we can't open an
 
319
     * existing file, assume the system is out of resources or that it is
 
320
     * mis-configured, and force backoff by raising a fatal error.
 
321
     */
 
322
    file_count = 0;
 
323
    for (qp = queue_info; qp->name != 0; qp++) {
 
324
        SCAN_DIR *scan = scan_dir_open(qp->name);
 
325
        char   *saved_id = 0;
 
326
 
 
327
        while ((id = qp->scan_next(scan)) != 0) {
 
328
 
 
329
            /*
 
330
             * XXX I have seen showq loop on the same queue id. That would be
 
331
             * an operating system bug, but who cares whose fault it is. Make
 
332
             * sure this will never happen again.
 
333
             */
 
334
            if (saved_id) {
 
335
                if (strcmp(saved_id, id) == 0) {
 
336
                    msg_warn("readdir loop on queue %s id %s", qp->name, id);
 
337
                    break;
 
338
                }
 
339
                myfree(saved_id);
 
340
            }
 
341
            saved_id = mystrdup(id);
 
342
            status = mail_open_ok(qp->name, id, &st, &path);
 
343
            if (status == MAIL_OPEN_YES) {
 
344
                if (file_count == 0)
 
345
                    vstream_fprintf(client, STRING_FORMAT,
 
346
                                    "-Queue ID-", "--Size--",
 
347
                                    "----Arrival Time----",
 
348
                                    "-Sender/Recipient-------");
 
349
                else
 
350
                    vstream_fprintf(client, "\n");
 
351
                if ((qfile = mail_queue_open(qp->name, id, O_RDONLY, 0)) != 0) {
 
352
                    queue_size += st.st_size;
 
353
                    showq_report(client, qp->name, id, qfile, (long) st.st_size,
 
354
                                 st.st_mtime);
 
355
                    if (vstream_fclose(qfile))
 
356
                        msg_warn("close file %s %s: %m", qp->name, id);
 
357
                } else if (strcmp(qp->name, MAIL_QUEUE_MAILDROP) == 0) {
 
358
                    queue_size += st.st_size;
 
359
                    vstream_fprintf(client, DROP_FORMAT, id, ' ',
 
360
                                    (long) st.st_size,
 
361
                                    asctime(localtime(&st.st_mtime)),
 
362
                                    (unsigned) st.st_uid);
 
363
                } else if (errno != ENOENT)
 
364
                    msg_fatal("open %s %s: %m", qp->name, id);
 
365
                file_count++;
 
366
                vstream_fflush(client);
 
367
            }
 
368
            vstream_fflush(client);
 
369
        }
 
370
        if (saved_id)
 
371
            myfree(saved_id);
 
372
        scan_dir_close(scan);
 
373
    }
 
374
    if (file_count == 0)
 
375
        vstream_fprintf(client, "Mail queue is empty\n");
 
376
    else {
 
377
        vstream_fprintf(client, "\n-- %lu Kbytes in %d Request%s.\n",
 
378
                        queue_size / 1024, file_count,
 
379
                        file_count == 1 ? "" : "s");
 
380
    }
 
381
}
 
382
 
 
383
/* main - pass control to the single-threaded server skeleton */
 
384
 
 
385
int     main(int argc, char **argv)
 
386
{
 
387
    static CONFIG_INT_TABLE int_table[] = {
 
388
        VAR_DUP_FILTER_LIMIT, DEF_DUP_FILTER_LIMIT, &var_dup_filter_limit, 0, 0,
 
389
        0,
 
390
    };
 
391
    CONFIG_STR_TABLE str_table[] = {
 
392
        VAR_EMPTY_ADDR, DEF_EMPTY_ADDR, &var_empty_addr, 1, 0,
 
393
        0,
 
394
    };
 
395
 
 
396
    single_server_main(argc, argv, showq_service,
 
397
                       MAIL_SERVER_INT_TABLE, int_table,
 
398
                       MAIL_SERVER_STR_TABLE, str_table,
 
399
                       0);
 
400
}