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