Copyright updates:
[exim.git] / src / src / lookups / lf_check_file.c
1 /*************************************************
2 *     Exim - an Internet mail transport agent    *
3 *************************************************/
4
5 /* Copyright (c) University of Cambridge 1995 - 2009 */
6 /* Copyright (c) The Exim Maintainers 2020 */
7 /* See the file NOTICE for conditions of use and distribution. */
8
9
10 #include "../exim.h"
11 #include "lf_functions.h"
12
13
14
15 /*************************************************
16 *         Check a file's credentials             *
17 *************************************************/
18
19 /* fstat can normally be expected to work on an open file, but there are some
20 NFS states where it may not.
21
22 Arguments:
23   fd         an open file descriptor or -1
24   filename   a file name if fd is -1
25   s_type     type of file (S_IFREG or S_IFDIR)
26   modemask   a mask specifying mode bits that must *not* be set
27   owners     NULL or a list of of allowable uids, count in the first item
28   owngroups  NULL or a list of allowable gids, count in the first item
29   type       name of lookup type for putting in error message
30   errmsg     where to put an error message
31
32 Returns:     -1 stat() or fstat() failed
33               0 OK
34              +1 something didn't match
35
36 Side effect: sets errno to ERRNO_BADUGID, ERRNO_NOTREGULAR or ERRNO_BADMODE for
37              bad uid/gid, not a regular file, or bad mode; otherwise leaves it
38              to what fstat set it to.
39 */
40
41 int
42 lf_check_file(int fd, const uschar * filename, int s_type, int modemask,
43   uid_t * owners, gid_t * owngroups, const char * type, uschar ** errmsg)
44 {
45 struct stat statbuf;
46
47 if ((fd >= 0 && fstat(fd, &statbuf) != 0) ||
48     (fd  < 0 && Ustat(filename, &statbuf) != 0))
49   {
50   int save_errno = errno;
51   *errmsg = string_sprintf("%s: stat failed", filename);
52   errno = save_errno;
53   return -1;
54   }
55
56 if ((statbuf.st_mode & S_IFMT) != s_type)
57   {
58   if (s_type == S_IFREG)
59     {
60     *errmsg = string_sprintf("%s is not a regular file (%s lookup)",
61       filename, type);
62     errno = ERRNO_NOTREGULAR;
63     }
64   else
65     {
66     *errmsg = string_sprintf("%s is not a directory (%s lookup)",
67       filename, type);
68     errno = ERRNO_NOTDIRECTORY;
69     }
70   return +1;
71   }
72
73 if ((statbuf.st_mode & modemask) != 0)
74   {
75   *errmsg = string_sprintf("%s (%s lookup): file mode %.4o should not contain "
76     "%.4o", filename, type,  statbuf.st_mode & 07777,
77     statbuf.st_mode & modemask);
78   errno = ERRNO_BADMODE;
79   return +1;
80   }
81
82 if (owners != NULL)
83   {
84   BOOL uid_ok = FALSE;
85   for (int i = 1; i <= (int)owners[0]; i++)
86     if (owners[i] == statbuf.st_uid) { uid_ok = TRUE; break; }
87   if (!uid_ok)
88     {
89     *errmsg = string_sprintf("%s (%s lookup): file has wrong owner", filename,
90       type);
91     errno = ERRNO_BADUGID;
92     return +1;
93     }
94   }
95
96 if (owngroups != NULL)
97   {
98   BOOL gid_ok = FALSE;
99   for (int i = 1; i <= (int)owngroups[0]; i++)
100     if (owngroups[i] == statbuf.st_gid) { gid_ok = TRUE; break; }
101   if (!gid_ok)
102     {
103     *errmsg = string_sprintf("%s (%s lookup): file has wrong group", filename,
104       type);
105     errno = ERRNO_BADUGID;
106     return +1;
107     }
108   }
109
110 return 0;
111 }
112
113 /* End of lf_check_file.c */