Commit dfed0be6 dfed0be6c0fb0541234422af700e955b77d09bd4 by Sergey Poznyakoff

Provide a mechanism for configurable file safety checking.

Use it in maidag (.forward file safety) and in libmu_auth (TLS files).

* include/mailutils/tls.h (mu_tls_module_config): New members
for safety check flags.
* include/mailutils/util.h (MU_FILE_SAFETY_NONE,MU_FILE_SAFETY_ALL)
(MU_FILE_SAFETY_OWNER_MISMATCH)
(MU_FILE_SAFETY_GROUP_WRITABLE,MU_FILE_SAFETY_WORLD_WRITABLE)
(MU_FILE_SAFETY_GROUP_READABLE,MU_FILE_SAFETY_WORLD_READABLE)
(MU_FILE_SAFETY_LINKED_WRDIR)
(MU_FILE_SAFETY_DIR_IWGRP,MU_FILE_SAFETY_DIR_IWOTH): New constants.
(mu_file_safety_check,mu_file_safety_name_to_code)
(mu_file_safety_name_to_error): New protos.
* libmailutils/base/checkperms.c: New file.
* libmailutils/base/Makefile.am (libbase_la_SOURCES): Add checkperms.c.
* libmailutils/diag/errors: Add new error codes.
(MU_ERR_UNSAFE_PERMS): change description wording.
* libmu_auth/tls.c (mu_check_tls_environment): Use mu_file_safety_check.
* libmu_cfg/tls.c (tls_settings): Initialize.
(mu_tls_param): New configuration file statements:
key-file-safety-checks, cert-file-safety-checks, ca-file-safety-checks.
* maidag/forward.c (maidag_forward): Use mu_file_safety_check.
* maidag/maidag.c (cb2_forward_file_checks): Use
mu_file_safety_name_to_code.
1 parent 502c6353
...@@ -28,9 +28,15 @@ extern "C" { ...@@ -28,9 +28,15 @@ extern "C" {
28 struct mu_tls_module_config 28 struct mu_tls_module_config
29 { 29 {
30 int enable; 30 int enable;
31
31 char *ssl_cert; 32 char *ssl_cert;
33 int ssl_cert_safety_checks;
34
32 char *ssl_key; 35 char *ssl_key;
36 int ssl_key_safety_checks;
37
33 char *ssl_cafile; 38 char *ssl_cafile;
39 int ssl_cafile_safety_checks;
34 }; 40 };
35 41
36 extern int mu_tls_module_init (enum mu_gocs_op, void *); 42 extern int mu_tls_module_init (enum mu_gocs_op, void *);
......
...@@ -199,6 +199,35 @@ void mu_onexit_reset (void); ...@@ -199,6 +199,35 @@ void mu_onexit_reset (void);
199 /* Register the onexit function and associated data */ 199 /* Register the onexit function and associated data */
200 int mu_onexit (mu_onexit_t func, void *data); 200 int mu_onexit (mu_onexit_t func, void *data);
201 201
202 #define MU_FILE_SAFETY_NONE 0x00
203 #define MU_FILE_SAFETY_OWNER_MISMATCH 0x01
204 #define MU_FILE_SAFETY_GROUP_WRITABLE 0x02
205 #define MU_FILE_SAFETY_WORLD_WRITABLE 0x04
206 #define MU_FILE_SAFETY_GROUP_READABLE 0x08
207 #define MU_FILE_SAFETY_WORLD_READABLE 0x10
208 #define MU_FILE_SAFETY_LINKED_WRDIR 0x20
209 #define MU_FILE_SAFETY_DIR_IWGRP 0x40
210 #define MU_FILE_SAFETY_DIR_IWOTH 0x80
211
212 #define MU_FILE_SAFETY_ALL ( \
213 MU_FILE_SAFETY_OWNER_MISMATCH | \
214 MU_FILE_SAFETY_GROUP_WRITABLE | \
215 MU_FILE_SAFETY_WORLD_WRITABLE | \
216 MU_FILE_SAFETY_GROUP_READABLE | \
217 MU_FILE_SAFETY_WORLD_READABLE | \
218 MU_FILE_SAFETY_LINKED_WRDIR | \
219 MU_FILE_SAFETY_DIR_IWGRP | \
220 MU_FILE_SAFETY_DIR_IWOTH )
221
222
223 struct mu_auth_data;
224
225 int mu_file_safety_check (const char *filename, int mode,
226 struct mu_auth_data *auth,
227 mu_list_t idlist);
228 int mu_file_safety_name_to_code (const char *name, int *pcode);
229 int mu_file_safety_name_to_error (const char *name, int *pcode);
230
202 #ifdef __cplusplus 231 #ifdef __cplusplus
203 } 232 }
204 #endif 233 #endif
......
...@@ -24,6 +24,7 @@ libbase_la_SOURCES = \ ...@@ -24,6 +24,7 @@ libbase_la_SOURCES = \
24 argcvjoin.c\ 24 argcvjoin.c\
25 argcvrem.c\ 25 argcvrem.c\
26 assoc.c\ 26 assoc.c\
27 filesafety.c\
27 daemon.c\ 28 daemon.c\
28 date.c\ 29 date.c\
29 fdwait.c\ 30 fdwait.c\
......
1 /* File safety checks.
2 Copyright (C) 1999, 2000, 2001, 2003, 2004, 2005, 2006, 2010, 2011
3 Free Software Foundation, Inc.
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 3 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General
16 Public License along with this library. If not, see
17 <http://www.gnu.org/licenses/>. */
18
19 #ifdef HAVE_CONFIG_H
20 # include <config.h>
21 #endif
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <unistd.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <mailutils/types.h>
28 #include <mailutils/errno.h>
29 #include <mailutils/mu_auth.h>
30 #include <mailutils/list.h>
31 #include <mailutils/util.h>
32
33 /* Functions for checking mode of a file and the directory it resides in.
34 Each of these checks certain bits and returns 0 if they are OK
35 and non-0 otherwise. */
36
37 struct file_check_buffer
38 {
39 struct stat filst;
40 struct stat dirst;
41 int cdir;
42 };
43
44 static int
45 _check_grdfil (struct file_check_buffer *fb)
46 {
47 return fb->filst.st_mode & S_IRGRP;
48 }
49
50 static int
51 _check_ardfil (struct file_check_buffer *fb)
52 {
53 return fb->filst.st_mode & S_IROTH;
54 }
55
56 static int
57 _check_gwrfil (struct file_check_buffer *fb)
58 {
59 return fb->filst.st_mode & S_IWGRP;
60 }
61
62 static int
63 _check_awrfil (struct file_check_buffer *fb)
64 {
65 return fb->filst.st_mode & S_IWOTH;
66 }
67
68 static int
69 _check_linkwrdir (struct file_check_buffer *fb)
70 {
71 return (fb->filst.st_mode & S_IFLNK) &&
72 (fb->dirst.st_mode & (S_IWGRP | S_IWOTH));
73 }
74
75 static int
76 _check_gwrdir (struct file_check_buffer *fb)
77 {
78 return fb->dirst.st_mode & S_IWGRP;
79 }
80
81 static int
82 _check_awrdir (struct file_check_buffer *fb)
83 {
84 return fb->dirst.st_mode & S_IWOTH;
85 }
86
87 /* The table of permission checkers below has this type: */
88 struct safety_checker
89 {
90 char *name; /* Symbolic name */
91 int flag; /* MU_FILE_SAFETY_ flag that enables this entry */
92 int err; /* Corresponding error code */
93 int cdir; /* True if the function needs dirst member */
94 int (*fun) (struct file_check_buffer *fb); /* Checker function */
95 };
96
97 static struct safety_checker file_safety_check_tab[] = {
98 { "grdfil", MU_FILE_SAFETY_GROUP_READABLE, MU_ERR_PERM_GROUP_READABLE,
99 0, _check_grdfil },
100 { "ardfil", MU_FILE_SAFETY_WORLD_READABLE, MU_ERR_PERM_WORLD_READABLE,
101 0, _check_ardfil },
102 { "gwrfil", MU_FILE_SAFETY_GROUP_WRITABLE, MU_ERR_PERM_GROUP_WRITABLE,
103 0, _check_gwrfil },
104 { "awrfil", MU_FILE_SAFETY_WORLD_WRITABLE, MU_ERR_PERM_WORLD_WRITABLE,
105 0, _check_awrfil },
106 { "linkwrdir", MU_FILE_SAFETY_LINKED_WRDIR, MU_ERR_PERM_LINKED_WRDIR,
107 1, _check_linkwrdir },
108 { "gwrdir", MU_FILE_SAFETY_DIR_IWGRP, MU_ERR_PERM_DIR_IWGRP,
109 1, _check_gwrdir },
110 { "awrdir", MU_FILE_SAFETY_DIR_IWOTH, MU_ERR_PERM_DIR_IWOTH,
111 1, _check_awrdir },
112 { 0 }
113 };
114
115 struct file_id
116 {
117 dev_t dev;
118 ino_t inode;
119 };
120
121 static int
122 file_id_cmp (const void *item, const void *data)
123 {
124 const struct file_id *a = item;
125 const struct file_id *b = data;
126
127 if (a->dev != b->dev)
128 return 1;
129 if (a->inode != b->inode)
130 return 1;
131 return 0;
132 }
133
134 static int
135 file_id_lookup (mu_list_t idlist, dev_t dev, ino_t ino)
136 {
137 struct file_id id;
138
139 id.dev = dev;
140 id.inode = ino;
141 return mu_list_locate (idlist, &id, NULL);
142 }
143
144 static int
145 file_id_remember (mu_list_t idlist, dev_t dev, ino_t ino)
146 {
147 struct file_id *id = malloc (sizeof (*id));
148 if (!id)
149 {
150 mu_error ("%s", mu_strerror (errno));
151 return 1;
152 }
153 id->dev = dev;
154 id->inode = ino;
155 return mu_list_append (idlist, id);
156 }
157
158 static struct safety_checker *
159 _find_safety_checker (const char *name)
160 {
161 struct safety_checker *pck;
162 for (pck = file_safety_check_tab; pck->flag; pck++)
163 if (strcmp (pck->name, name) == 0)
164 return pck;
165 return NULL;
166 }
167
168 int
169 mu_file_safety_name_to_code (const char *name, int *pcode)
170 {
171 struct safety_checker *pck = _find_safety_checker (name);
172 if (pck)
173 {
174 *pcode = pck->flag;
175 return 0;
176 }
177 return MU_ERR_NOENT;
178 }
179
180 int
181 mu_file_safety_name_to_error (const char *name, int *pcode)
182 {
183 struct safety_checker *pck = _find_safety_checker (name);
184 if (pck)
185 {
186 *pcode = pck->err;
187 return 0;
188 }
189 return MU_ERR_NOENT;
190 }
191
192 int
193 mu_file_safety_check (const char *filename, int mode,
194 struct mu_auth_data *auth,
195 mu_list_t idlist)
196 {
197 struct file_check_buffer buf;
198
199 memset (&buf, 0, sizeof (buf));
200 if (stat (filename, &buf.filst) == 0)
201 {
202 struct safety_checker *pck;
203
204 if (idlist)
205 {
206 mu_list_set_destroy_item (idlist, mu_list_free_item);
207 mu_list_set_comparator (idlist, file_id_cmp);
208 if (file_id_lookup (idlist, buf.filst.st_dev, buf.filst.st_ino) == 0)
209 return MU_ERR_EXISTS;
210 }
211
212 if ((mode & MU_FILE_SAFETY_OWNER_MISMATCH) &&
213 auth &&
214 auth->uid != buf.filst.st_uid)
215 return MU_ERR_PERM_OWNER_MISMATCH;
216
217 for (pck = file_safety_check_tab; pck->flag; pck++)
218 if (mode & pck->flag)
219 {
220 if (pck->cdir && !buf.cdir)
221 {
222 char *dirname, *p;
223
224 p = strrchr (filename, '/');
225 if (!p)
226 dirname = strdup (".");
227 else if (p == filename)
228 dirname = strdup ("/");
229 else
230 {
231 size_t len = p - filename;
232 dirname = malloc (len + 1);
233 if (dirname)
234 {
235 memcpy (dirname, filename, len);
236 dirname[len] = 0;
237 }
238 }
239 if (!dirname)
240 return ENOMEM;
241 if (stat (dirname, &buf.dirst))
242 return errno;
243 buf.cdir = 1;
244 }
245 if (pck->fun (&buf))
246 return pck->err;
247 }
248 if (idlist)
249 file_id_remember (idlist, buf.filst.st_dev, buf.filst.st_ino);
250 return 0;
251 }
252 return errno;
253 }
...@@ -65,7 +65,6 @@ MU_ERR_BADREPLY _("Invalid reply from the remote host") ...@@ -65,7 +65,6 @@ MU_ERR_BADREPLY _("Invalid reply from the remote host")
65 MU_ERR_SEQ _("Bad command sequence") 65 MU_ERR_SEQ _("Bad command sequence")
66 MU_ERR_REPLY _("Erroneous reply received") 66 MU_ERR_REPLY _("Erroneous reply received")
67 67
68 MU_ERR_UNSAFE_PERMS _("Unsafe file permissions. Set 0600")
69 MU_ERR_BAD_AUTH_SCHEME _("Unsupported authentication scheme") 68 MU_ERR_BAD_AUTH_SCHEME _("Unsupported authentication scheme")
70 MU_ERR_AUTH_FAILURE _("Authentication failed") 69 MU_ERR_AUTH_FAILURE _("Authentication failed")
71 70
...@@ -106,3 +105,16 @@ MU_ERR_BADFLAGS _("Bad value for flags") ...@@ -106,3 +105,16 @@ MU_ERR_BADFLAGS _("Bad value for flags")
106 MU_ERR_SOCKTYPE _("Socket type not supported") 105 MU_ERR_SOCKTYPE _("Socket type not supported")
107 MU_ERR_FAMILY _("Address family not supported") 106 MU_ERR_FAMILY _("Address family not supported")
108 MU_ERR_SERVICE _("Requested service not supported") 107 MU_ERR_SERVICE _("Requested service not supported")
108
109 # File safety check
110 MU_ERR_PERM_OWNER_MISMATCH _("File owner mismatch")
111 MU_ERR_PERM_GROUP_WRITABLE _("Group writable file")
112 MU_ERR_PERM_WORLD_WRITABLE _("World writable file")
113 MU_ERR_PERM_GROUP_READABLE _("Group readable file")
114 MU_ERR_PERM_WORLD_READABLE _("World readable file")
115 MU_ERR_PERM_LINKED_WRDIR _("Linked file in a writable directory")
116 MU_ERR_PERM_DIR_IWGRP _("File in group writable directory")
117 MU_ERR_PERM_DIR_IWOTH _("File in world writable directory")
118
119 # A simpler version of the above. Possibly will be removed in the future.
120 MU_ERR_UNSAFE_PERMS _("Unsafe file permissions")
......
...@@ -33,8 +33,9 @@ ...@@ -33,8 +33,9 @@
33 #include <mailutils/nls.h> 33 #include <mailutils/nls.h>
34 #include <mailutils/stream.h> 34 #include <mailutils/stream.h>
35 #include <mailutils/errno.h> 35 #include <mailutils/errno.h>
36 #include <mailutils/util.h>
36 37
37 struct mu_tls_module_config mu_tls_module_config = { 1, NULL, NULL, NULL }; 38 struct mu_tls_module_config mu_tls_module_config;
38 39
39 int 40 int
40 mu_tls_module_init (enum mu_gocs_op op, void *data) 41 mu_tls_module_init (enum mu_gocs_op op, void *data)
...@@ -65,44 +66,38 @@ mu_tls_module_init (enum mu_gocs_op op, void *data) ...@@ -65,44 +66,38 @@ mu_tls_module_init (enum mu_gocs_op op, void *data)
65 static gnutls_dh_params dh_params; 66 static gnutls_dh_params dh_params;
66 static gnutls_certificate_server_credentials x509_cred; 67 static gnutls_certificate_server_credentials x509_cred;
67 68
69 /* Return: zero means NOT READY, one means READY */
68 int 70 int
69 mu_check_tls_environment (void) 71 mu_check_tls_environment (void)
70 { 72 {
71 /* Return: zero means NOT READY, one means READY */
72
73 if (mu_tls_module_config.ssl_cert && mu_tls_module_config.ssl_key) 73 if (mu_tls_module_config.ssl_cert && mu_tls_module_config.ssl_key)
74 { 74 {
75 struct stat st; 75 int rc = mu_file_safety_check (mu_tls_module_config.ssl_cert,
76 76 mu_tls_module_config.ssl_cert_safety_checks,
77 if (stat (mu_tls_module_config.ssl_cert, &st) == -1) 77 NULL, NULL);
78 if (rc)
78 { 79 {
79 mu_error ("%s: %s.", mu_tls_module_config.ssl_cert, 80 mu_error ("%s: %s", mu_tls_module_config.ssl_cert,
80 mu_strerror (errno)); 81 mu_strerror (rc));
81 return 0; 82 return 0;
82 } 83 }
83 if (!(st.st_mode & S_IFREG) || !(st.st_mode & S_IFLNK)) 84 rc = mu_file_safety_check (mu_tls_module_config.ssl_key,
85 mu_tls_module_config.ssl_key_safety_checks,
86 NULL, NULL);
87 if (rc)
84 { 88 {
85 mu_error (_("%s is not a regular file or a symbolic link."), 89 mu_error ("%s: %s", mu_tls_module_config.ssl_key,
86 mu_tls_module_config.ssl_cert); 90 mu_strerror (rc));
87 return 0; 91 return 0;
88 } 92 }
89 93
90 if (stat (mu_tls_module_config.ssl_key, &st) == -1) 94 rc = mu_file_safety_check (mu_tls_module_config.ssl_cafile,
91 { 95 mu_tls_module_config.ssl_cafile_safety_checks,
92 mu_error ("%s: %s.", mu_tls_module_config.ssl_key, 96 NULL, NULL);
93 mu_strerror(errno)); 97 if (rc)
94 return 0;
95 }
96 if (!(st.st_mode & S_IFREG) || !(st.st_mode & S_IFLNK))
97 {
98 mu_error (_("%s is not a regular file or a symbolic link."),
99 mu_tls_module_config.ssl_key);
100 return 0;
101 }
102 if ((st.st_mode & S_IRWXG) || (st.st_mode & S_IRWXO))
103 { 98 {
104 mu_error (_("wrong permissions on %s (set 0600)"), 99 mu_error ("%s: %s", mu_tls_module_config.ssl_cafile,
105 mu_tls_module_config.ssl_key); 100 mu_strerror (rc));
106 return 0; 101 return 0;
107 } 102 }
108 } 103 }
......
...@@ -21,8 +21,65 @@ ...@@ -21,8 +21,65 @@
21 #include <stdlib.h> 21 #include <stdlib.h>
22 #include "mailutils/libcfg.h" 22 #include "mailutils/libcfg.h"
23 #include <mailutils/tls.h> 23 #include <mailutils/tls.h>
24 #include <mailutils/util.h>
25 #include <mailutils/kwd.h>
24 26
25 static struct mu_tls_module_config tls_settings; 27 static struct mu_tls_module_config tls_settings = {
28 1, /* enabled by default */
29
30 NULL, /* Certificate file */
31 MU_FILE_SAFETY_GROUP_WRITABLE |
32 MU_FILE_SAFETY_GROUP_WRITABLE |
33 MU_FILE_SAFETY_LINKED_WRDIR,
34
35 NULL, /* Key file */
36 MU_FILE_SAFETY_ALL, /* Stringent safety checks for keys */
37
38 NULL, /* CA file */
39 MU_FILE_SAFETY_GROUP_WRITABLE |
40 MU_FILE_SAFETY_GROUP_WRITABLE |
41 MU_FILE_SAFETY_LINKED_WRDIR
42 };
43
44
45 static int
46 cb2_safety_checks (const char *name, void *data)
47 {
48 int negate = 0;
49 int val;
50 int *res = data;
51
52 if (strcmp (name, "none") == 0)
53 {
54 *res = MU_FILE_SAFETY_NONE;
55 return 0;
56 }
57
58 if (*name == '-')
59 {
60 negate = 1;
61 name++;
62 }
63 else if (*name == '+')
64 name++;
65
66 if (mu_file_safety_name_to_code (name, &val))
67 mu_error (_("unknown keyword: %s"), name);
68 else
69 {
70 if (negate)
71 *res &= ~val;
72 else
73 *res |= val;
74 }
75 return 0;
76 }
77
78 static int
79 cb_safety_checks (void *data, mu_config_value_t *arg)
80 {
81 return mu_cfg_string_value_cb (arg, cb2_safety_checks, data);
82 }
26 83
27 static struct mu_cfg_param mu_tls_param[] = { 84 static struct mu_cfg_param mu_tls_param[] = {
28 { "enable", mu_cfg_bool, &tls_settings.enable, 0, NULL, 85 { "enable", mu_cfg_bool, &tls_settings.enable, 0, NULL,
...@@ -36,6 +93,33 @@ static struct mu_cfg_param mu_tls_param[] = { ...@@ -36,6 +93,33 @@ static struct mu_cfg_param mu_tls_param[] = {
36 { "ssl-cafile", mu_cfg_string, &tls_settings.ssl_cafile, 0, NULL, 93 { "ssl-cafile", mu_cfg_string, &tls_settings.ssl_cafile, 0, NULL,
37 N_("Specify trusted CAs file."), 94 N_("Specify trusted CAs file."),
38 N_("file") }, 95 N_("file") },
96 { "key-file-safety-checks", mu_cfg_callback,
97 &tls_settings.ssl_key_safety_checks, 0,
98 cb_safety_checks,
99 N_("Configure safety checks for SSL key file. Argument is a list or "
100 "sequence of check names optionally prefixed with '+' to enable or "
101 "'-' to disable the corresponding check. Valid check names are:\n"
102 "\n"
103 " none disable all checks\n"
104 " all enable all checks\n"
105 " gwrfil forbid group writable files\n"
106 " awrfil forbid world writable files\n"
107 " grdfil forbid group readable files\n"
108 " ardfil forbid world writable files\n"
109 " linkwrdir forbid symbolic links in group or world writable directories\n"
110 " gwrdir forbid files in group writable directories\n"
111 " awrdir forbid files in world writable directories\n"),
112 N_("arg: list") },
113 { "cert-file-safety-checks", mu_cfg_callback,
114 &tls_settings.ssl_cert_safety_checks, 0,
115 cb_safety_checks,
116 N_("Configure safety checks for SSL certificate. See above for a description of <arg>."),
117 N_("arg: list") },
118 { "ca-file-safety-checks", mu_cfg_callback,
119 &tls_settings.ssl_cafile_safety_checks, 0,
120 cb_safety_checks,
121 N_("Configure safety checks for SSL certificate authority file. See above for a description of <arg>."),
122 N_("arg: list") },
39 { NULL } 123 { NULL }
40 }; 124 };
41 125
......
...@@ -19,154 +19,6 @@ ...@@ -19,154 +19,6 @@
19 19
20 #include "maidag.h" 20 #include "maidag.h"
21 21
22 /* Functions for checking file mode of .forward and its directory.
23 Each of these checks certain bits and returns 0 if they are OK
24 and non-0 otherwise. */
25
26 static int
27 check_iwgrp (struct stat *filest, struct stat *dirst)
28 {
29 return filest->st_mode & S_IWGRP;
30 }
31
32 static int
33 check_iwoth (struct stat *filest, struct stat *dirst)
34 {
35 return filest->st_mode & S_IWOTH;
36 }
37
38 static int
39 check_linked_wrdir (struct stat *filest, struct stat *dirst)
40 {
41 return (filest->st_mode & S_IFLNK) && (dirst->st_mode & (S_IWGRP | S_IWOTH));
42 }
43
44 static int
45 check_dir_iwgrp (struct stat *filest, struct stat *dirst)
46 {
47 return dirst->st_mode & S_IWGRP;
48 }
49
50 static int
51 check_dir_iwoth (struct stat *filest, struct stat *dirst)
52 {
53 return dirst->st_mode & S_IWOTH;
54 }
55
56 /* The table of permission checkers below has this type: */
57 struct perm_checker
58 {
59 int flag; /* FWD_ flag that enables this entry */
60 char *descr; /* Textual description to use if FUN returns !0 */
61 int (*fun) (struct stat *filest, struct stat *dirst); /* Checker function */
62 };
63
64 static struct perm_checker perm_check_tab[] = {
65 { FWD_IWGRP, N_("group writable forward file"), check_iwgrp },
66 { FWD_IWOTH, N_("world writable forward file"), check_iwoth },
67 { FWD_LINK, N_("linked forward file in writable dir"), check_linked_wrdir },
68 { FWD_DIR_IWGRP, N_("forward file in group writable directory"),
69 check_dir_iwgrp },
70 { FWD_DIR_IWOTH, N_("forward file in world writable directory"),
71 check_dir_iwoth },
72 { 0 }
73 };
74
75 static mu_list_t idlist;
76
77 struct file_id
78 {
79 dev_t dev;
80 ino_t inode;
81 };
82
83 static int
84 file_id_cmp (const void *item, const void *data)
85 {
86 const struct file_id *a = item;
87 const struct file_id *b = data;
88
89 if (a->dev != b->dev)
90 return 1;
91 if (a->inode != b->inode)
92 return 1;
93 return 0;
94 }
95
96 static int
97 file_id_lookup (dev_t dev, ino_t ino)
98 {
99 struct file_id id;
100
101 id.dev = dev;
102 id.inode = ino;
103 return mu_list_locate (idlist, &id, NULL);
104 }
105
106 static int
107 file_id_remember (dev_t dev, ino_t ino)
108 {
109 struct file_id *id = malloc (sizeof (*id));
110 if (!id)
111 {
112 mu_error ("%s", mu_strerror (errno));
113 return 1;
114 }
115 id->dev = dev;
116 id->inode = ino;
117 return mu_list_append (idlist, id);
118 }
119
120
121 /* Check if the forwrd file FILENAME has right permissions and file mode.
122 DIRST describes the directory holding the file, AUTH gives current user
123 authority. */
124 int
125 check_forward_permissions (const char *filename, struct stat *dirst,
126 struct mu_auth_data *auth)
127 {
128 struct stat st;
129
130 if (stat (filename, &st) == 0)
131 {
132 int i;
133
134 if (!idlist)
135 {
136 mu_list_create (&idlist);
137 mu_list_set_comparator (idlist, file_id_cmp);
138 }
139 else if (file_id_lookup (st.st_dev, st.st_ino) == 0)
140 {
141 mu_diag_output (MU_DIAG_NOTICE,
142 _("skipping forward file %s: already processed"),
143 filename);
144 return 1;
145 }
146
147 if ((forward_file_checks & FWD_OWNER) &&
148 auth->uid != st.st_uid)
149 {
150 mu_error (_("%s not owned by %s"), filename, auth->name);
151 return 1;
152 }
153 for (i = 0; perm_check_tab[i].flag; i++)
154 if ((forward_file_checks & perm_check_tab[i].flag)
155 && perm_check_tab[i].fun (&st, dirst))
156 {
157 mu_error ("%s: %s", filename, gettext (perm_check_tab[i].descr));
158 return 1;
159 }
160 file_id_remember (st.st_dev, st.st_ino);
161 return 0;
162 }
163 else if (errno != ENOENT)
164 mu_error (_("%s: cannot stat file: %s"),
165 filename, mu_strerror (errno));
166 return 1;
167 }
168
169
170 /* Auxiliary functions */ 22 /* Auxiliary functions */
171 23
172 /* Forward message MSG to given EMAIL, using MAILER and sender address FROM */ 24 /* Forward message MSG to given EMAIL, using MAILER and sender address FROM */
...@@ -320,6 +172,9 @@ process_forward (mu_message_t msg, char *filename, const char *myname) ...@@ -320,6 +172,9 @@ process_forward (mu_message_t msg, char *filename, const char *myname)
320 return result; 172 return result;
321 } 173 }
322 174
175
176 static mu_list_t idlist;
177
323 /* Check if the forward file FWFILE for user given by AUTH exists, and if 178 /* Check if the forward file FWFILE for user given by AUTH exists, and if
324 so, use to to forward message MSG. */ 179 so, use to to forward message MSG. */
325 enum maidag_forward_result 180 enum maidag_forward_result
...@@ -328,7 +183,8 @@ maidag_forward (mu_message_t msg, struct mu_auth_data *auth, char *fwfile) ...@@ -328,7 +183,8 @@ maidag_forward (mu_message_t msg, struct mu_auth_data *auth, char *fwfile)
328 struct stat st; 183 struct stat st;
329 char *filename; 184 char *filename;
330 enum maidag_forward_result result = maidag_forward_none; 185 enum maidag_forward_result result = maidag_forward_none;
331 186 int rc;
187
332 if (fwfile[0] != '/') 188 if (fwfile[0] != '/')
333 { 189 {
334 if (stat (auth->dir, &st)) 190 if (stat (auth->dir, &st))
...@@ -353,8 +209,20 @@ maidag_forward (mu_message_t msg, struct mu_auth_data *auth, char *fwfile) ...@@ -353,8 +209,20 @@ maidag_forward (mu_message_t msg, struct mu_auth_data *auth, char *fwfile)
353 return maidag_forward_error; 209 return maidag_forward_error;
354 } 210 }
355 211
356 if (check_forward_permissions (filename, &st, auth) == 0) 212 if (!idlist)
213 mu_list_create (&idlist);
214
215 rc = mu_file_safety_check (filename, forward_file_checks,
216 auth, idlist);
217 if (rc == 0)
357 result = process_forward (msg, filename, auth->name); 218 result = process_forward (msg, filename, auth->name);
219 else if (rc)
220 mu_diag_output (MU_DIAG_NOTICE,
221 _("skipping forward file %s: already processed"),
222 filename);
223 else
224 mu_error (_("ignoring forward file %s: %s"),
225 filename, mu_strerror (rc));
358 226
359 free (filename); 227 free (filename);
360 return result; 228 return result;
......
...@@ -36,7 +36,14 @@ maidag_script_fun script_handler; ...@@ -36,7 +36,14 @@ maidag_script_fun script_handler;
36 mu_list_t script_list; 36 mu_list_t script_list;
37 37
38 char *forward_file = NULL; 38 char *forward_file = NULL;
39 int forward_file_checks = FWD_ALL; 39 #define FORWARD_FILE_PERM_CHECK ( \
40 MU_FILE_SAFETY_OWNER_MISMATCH | \
41 MU_FILE_SAFETY_GROUP_WRITABLE | \
42 MU_FILE_SAFETY_WORLD_WRITABLE | \
43 MU_FILE_SAFETY_LINKED_WRDIR | \
44 MU_FILE_SAFETY_DIR_IWGRP | \
45 MU_FILE_SAFETY_DIR_IWOTH )
46 int forward_file_checks = FORWARD_FILE_PERM_CHECK;
40 47
41 /* Debuggig options */ 48 /* Debuggig options */
42 int debug_level; /* General debugging level */ 49 int debug_level; /* General debugging level */
...@@ -326,44 +333,32 @@ cb_group (void *data, mu_config_value_t *arg) ...@@ -326,44 +333,32 @@ cb_group (void *data, mu_config_value_t *arg)
326 return mu_cfg_string_value_cb (arg, cb2_group, *plist); 333 return mu_cfg_string_value_cb (arg, cb2_group, *plist);
327 } 334 }
328 335
329 static struct mu_kwd forward_checks[] = {
330 { "all", FWD_ALL },
331 { "owner", FWD_OWNER },
332 { "groupwritablefile", FWD_IWGRP },
333 { "file_iwgrp", FWD_IWGRP },
334 { "worldwritablefile", FWD_IWOTH },
335 { "file_iwoth", FWD_IWOTH },
336 { "linkedfileinwritabledir", FWD_LINK },
337 { "link", FWD_LINK },
338 { "fileingroupwritabledir", FWD_DIR_IWGRP },
339 { "dir_iwgrp", FWD_DIR_IWGRP },
340 { "fileinworldwritabledir", FWD_DIR_IWOTH },
341 { "dir_iwoth", FWD_DIR_IWOTH },
342 { NULL }
343 };
344
345 static int 336 static int
346 cb2_forward_file_checks (const char *name, void *data) 337 cb2_forward_file_checks (const char *name, void *data)
347 { 338 {
348 int negate = 0;
349 const char *str;
350 int val; 339 int val;
351 340 int negate = 0;
341
342 if (strcmp (name, "all") == 0)
343 {
344 forward_file_checks = FORWARD_FILE_PERM_CHECK;
345 return 0;
346 }
352 if (strcmp (name, "none") == 0) 347 if (strcmp (name, "none") == 0)
353 { 348 {
354 forward_file_checks = 0; 349 forward_file_checks = 0;
355 return 0; 350 return 0;
356 } 351 }
357 352
358 if (strlen (name) > 2 && mu_c_strncasecmp (name, "no", 2) == 0) 353 if (*name == '-')
359 { 354 {
360 negate = 1; 355 negate = 1;
361 str = name + 2; 356 name++;
362 } 357 }
363 else 358 else if (*name == '+')
364 str = name; 359 name++;
365 360
366 if (mu_kwd_xlat_name_ci (forward_checks, str, &val)) 361 if (mu_file_safety_name_to_code (name, &val))
367 mu_error (_("unknown keyword: %s"), name); 362 mu_error (_("unknown keyword: %s"), name);
368 else 363 else
369 { 364 {
......
...@@ -109,15 +109,6 @@ extern int debug_level; ...@@ -109,15 +109,6 @@ extern int debug_level;
109 #define MAXFD 64 109 #define MAXFD 64
110 #define EX_QUOTA() (ex_quota_tempfail ? EX_TEMPFAIL : EX_UNAVAILABLE) 110 #define EX_QUOTA() (ex_quota_tempfail ? EX_TEMPFAIL : EX_UNAVAILABLE)
111 111
112 /* .forward file checks */
113 #define FWD_OWNER 0x0001 /* file ownership */
114 #define FWD_IWGRP 0x0002 /* group writable forward file */
115 #define FWD_IWOTH 0x0004 /* world writable forward file */
116 #define FWD_LINK 0x0008 /* linked forward file in writable dir */
117 #define FWD_DIR_IWGRP 0x0010 /* forward file in group writable directory */
118 #define FWD_DIR_IWOTH 0x0020 /* forward file in world writable directory */
119 #define FWD_ALL (FWD_OWNER|FWD_IWGRP|FWD_IWOTH|FWD_LINK|FWD_DIR_IWOTH|FWD_DIR_IWGRP)
120
121 enum maidag_mode 112 enum maidag_mode
122 { 113 {
123 mode_mda, 114 mode_mda,
......