1/*++
2/* NAME
3/*	mail_open_ok 3
4/* SUMMARY
5/*	scrutinize mail queue file
6/* SYNOPSIS
7/*	#include <mail_open_ok.h>
8/*
9/*	int	mail_open_ok(queue_name, queue_id, statp, pathp)
10/*	const char *queue_name;
11/*	const char *queue_id;
12/*	struct stat *statp;
13/*	char	**pathp
14/* DESCRIPTION
15/*	mail_open_ok() determines if it is OK to open the specified
16/*	queue file.
17/*
18/*	The queue name and queue id should conform to the syntax
19/*	requirements for these names.
20/*	Unfortunately, on some systems readdir() etc. can return bogus
21/*	file names. For this reason, the code silently ignores invalid
22/*	queue file names.
23/*
24/*	The file should have mode 0700. Files with other permissions
25/*	are silently ignored.
26/*
27/*	The file should be a regular file.
28/*	Files that do not satisfy this requirement are skipped with
29/*	a warning.
30/*
31/*	The file link count is not restricted. With a writable maildrop
32/*	directory, refusal to deliver linked files is prone to denial of
33/*	service attack; it's better to deliver mail too often than not.
34/*
35/*	Upon return, the \fIstatp\fR argument receives the file
36/*	attributes and \fIpathp\fR a copy of the file name. The file
37/*	name is volatile. Make a copy if it is to be used for any
38/*	appreciable amount of time.
39/* DIAGNOSTICS
40/*	Warnings: bad file attributes (file type), multiple hard links.
41/*	mail_open_ok() returns MAIL_OPEN_YES for good files, MAIL_OPEN_NO
42/*	for anything else. It is left up to the system administrator to
43/*	deal with non-file objects.
44/* BUGS
45/*	mail_open_ok() examines a queue file without actually opening
46/*	it, and therefore is susceptible to race conditions.
47/* LICENSE
48/* .ad
49/* .fi
50/*	The Secure Mailer license must be distributed with this software.
51/* AUTHOR(S)
52/*	Wietse Venema
53/*	IBM T.J. Watson Research
54/*	P.O. Box 704
55/*	Yorktown Heights, NY 10598, USA
56/*--*/
57
58/* System libraries. */
59
60#include <sys_defs.h>
61#include <sys/stat.h>
62#include <time.h>
63#include <errno.h>
64
65/* Utility library. */
66
67#include <msg.h>
68#include <warn_stat.h>
69
70/* Global library. */
71
72#include "mail_queue.h"
73#include "mail_open_ok.h"
74
75/* mail_open_ok - see if this file is OK to open */
76
77int     mail_open_ok(const char *queue_name, const char *queue_id,
78		             struct stat * statp, const char **path)
79{
80    if (mail_queue_name_ok(queue_name) == 0) {
81	msg_warn("bad mail queue name: %s", queue_name);
82	return (MAIL_OPEN_NO);
83    }
84    if (mail_queue_id_ok(queue_id) == 0)
85	return (MAIL_OPEN_NO);
86
87
88    /*
89     * I really would like to look up the file attributes *after* opening the
90     * file so that we could save one directory traversal on systems without
91     * name-to-inode cache. However, we don't necessarily always want to open
92     * the file.
93     */
94    *path = mail_queue_path((VSTRING *) 0, queue_name, queue_id);
95
96    if (lstat(*path, statp) < 0) {
97	if (errno != ENOENT)
98	    msg_warn("%s: %m", *path);
99	return (MAIL_OPEN_NO);
100    }
101    if (!S_ISREG(statp->st_mode)) {
102	msg_warn("%s: uid %ld: not a regular file", *path, (long) statp->st_uid);
103	return (MAIL_OPEN_NO);
104    }
105    if ((statp->st_mode & S_IRWXU) != MAIL_QUEUE_STAT_READY)
106	return (MAIL_OPEN_NO);
107
108    /*
109     * Workaround for spurious "file has 2 links" warnings in showq. As
110     * kernels have evolved from non-interruptible system calls towards
111     * fine-grained locks, the showq command has become likely to observe a
112     * file while the queue manager is in the middle of renaming it, at a
113     * time when the file has links to both the old and new name. We now log
114     * the warning only when the condition appears to be persistent.
115     */
116#define MINUTE_SECONDS	60			/* XXX should be centralized */
117
118    if (statp->st_nlink > 1) {
119	if (msg_verbose)
120	    msg_info("%s: uid %ld: file has %d links", *path,
121		     (long) statp->st_uid, (int) statp->st_nlink);
122	else if (statp->st_ctime < time((time_t *) 0) - MINUTE_SECONDS)
123	    msg_warn("%s: uid %ld: file has %d links", *path,
124		     (long) statp->st_uid, (int) statp->st_nlink);
125    }
126    return (MAIL_OPEN_YES);
127}
128