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.
Showing
10 changed files
with
446 additions
and
212 deletions
... | @@ -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 | ... | ... |
libmailutils/base/filesafety.c
0 → 100644
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, | ... | ... |
-
Please register or sign in to post a comment