1 /* $NetBSD: data_redirect.c,v 1.2 2017/02/14 01:16:45 christos Exp $ */
2
3 /*++
4 /* NAME
5 /* data_redirect 3
6 /* SUMMARY
7 /* redirect legacy writes to Postfix-owned data directory
8 /* SYNOPSIS
9 /* #include <data_redirect.h>
10 /*
11 /* char *data_redirect_file(result, path)
12 /* VSTRING *result;
13 /* const char *path;
14 /*
15 /* char *data_redirect_map(result, map)
16 /* VSTRING *result;
17 /* const char *map;
18 /* DESCRIPTION
19 /* With Postfix version 2.5 and later, the tlsmgr(8) and
20 /* verify(8) servers no longer open cache files with root
21 /* privilege. This avoids a potential security loophole where
22 /* the ownership of a file (or directory) does not match the
23 /* trust level of the content of that file (or directory).
24 /*
25 /* This module implements a migration aid that allows a
26 /* transition without disruption of service.
27 /*
28 /* data_redirect_file() detects a request to open a file in a
29 /* non-Postfix directory, logs a warning, and redirects the
30 /* request to the Postfix-owned data_directory.
31 /*
32 /* data_redirect_map() performs the same function for a limited
33 /* subset of file-based lookup tables.
34 /*
35 /* Arguments:
36 /* .IP result
37 /* A possibly redirected copy of the input.
38 /* .IP path
39 /* The pathname that may be redirected.
40 /* .IP map
41 /* The "mapname" or "maptype:mapname" that may be redirected.
42 /* The result is always in "maptype:mapname" form.
43 /* BUGS
44 /* Only a few map types are redirected. This is acceptable for
45 /* a temporary migration tool.
46 /* DIAGNOSTICS
47 /* Fatal errors: memory allocation failure.
48 /* CONFIGURATION PARAMETERS
49 /* data_directory, location of Postfix-writable files
50 /* LICENSE
51 /* .ad
52 /* .fi
53 /* The Secure Mailer license must be distributed with this software.
54 /* AUTHOR(S)
55 /* Wietse Venema
56 /* IBM T.J. Watson Research
57 /* P.O. Box 704
58 /* Yorktown Heights, NY 10598, USA
59 /*--*/
60
61 /* System library. */
62
63 #include <sys_defs.h>
64 #include <sys/stat.h>
65 #include <string.h>
66
67 /* Utility library. */
68
69 #include <msg.h>
70 #include <vstring.h>
71 #include <stringops.h>
72 #include <split_at.h>
73 #include <name_code.h>
74 #include <dict_db.h>
75 #include <dict_dbm.h>
76 #include <dict_cdb.h>
77 #include <dict_lmdb.h>
78 #include <warn_stat.h>
79
80 /* Global directory. */
81
82 #include <mail_params.h>
83 #include <dict_proxy.h>
84 #include <data_redirect.h>
85
86 /* Application-specific. */
87
88 #define STR(x) vstring_str(x)
89 #define LEN(x) VSTRING_LEN(x)
90
91 /*
92 * Redirect only these map types, so that we don't try stupid things with
93 * NIS, *SQL or LDAP. This is a transition feature for legacy TLS and verify
94 * configurations, so it does not have to cover every possible map type.
95 *
96 * XXX In this same spirit of imperfection we also use hard-coded map names,
97 * because maintainers may add map types that the official release doesn't
98 * even know about, because map types may be added dynamically on some
99 * platforms.
100 */
101 static const NAME_CODE data_redirect_map_types[] = {
102 DICT_TYPE_HASH, 1,
103 DICT_TYPE_BTREE, 1,
104 DICT_TYPE_DBM, 1,
105 DICT_TYPE_LMDB, 1,
106 DICT_TYPE_CDB, 1, /* not a read-write map type */
107 "sdbm", 1, /* legacy 3rd-party TLS */
108 "dbz", 1, /* just in case */
109 0, 0,
110 };
111
112 /* data_redirect_path - redirect path to Postfix-owned directory */
113
data_redirect_path(VSTRING * result,const char * path,const char * log_type,const char * log_name)114 static char *data_redirect_path(VSTRING *result, const char *path,
115 const char *log_type, const char *log_name)
116 {
117 struct stat st;
118
119 #define PATH_DELIMITER "/"
120
121 (void) sane_dirname(result, path);
122 if (stat(STR(result), &st) != 0 || st.st_uid == var_owner_uid) {
123 vstring_strcpy(result, path);
124 } else {
125 msg_warn("request to update %s %s in non-%s directory %s",
126 log_type, log_name, var_mail_owner, STR(result));
127 msg_warn("redirecting the request to %s-owned %s %s",
128 var_mail_owner, VAR_DATA_DIR, var_data_dir);
129 (void) sane_basename(result, path);
130 vstring_prepend(result, PATH_DELIMITER, sizeof(PATH_DELIMITER) - 1);
131 vstring_prepend(result, var_data_dir, strlen(var_data_dir));
132 }
133 return (STR(result));
134 }
135
136 /* data_redirect_file - redirect file to Postfix-owned directory */
137
data_redirect_file(VSTRING * result,const char * path)138 char *data_redirect_file(VSTRING *result, const char *path)
139 {
140
141 /*
142 * Sanity check.
143 */
144 if (path == STR(result))
145 msg_panic("data_redirect_file: result clobbers input");
146
147 return (data_redirect_path(result, path, "file", path));
148 }
149
data_redirect_map(VSTRING * result,const char * map)150 char *data_redirect_map(VSTRING *result, const char *map)
151 {
152 const char *path;
153 const char *map_type;
154 size_t map_type_len;
155
156 #define MAP_DELIMITER ":"
157
158 /*
159 * Sanity check.
160 */
161 if (map == STR(result))
162 msg_panic("data_redirect_map: result clobbers input");
163
164 /*
165 * Parse the input into map type and map name.
166 */
167 path = strchr(map, MAP_DELIMITER[0]);
168 if (path != 0) {
169 map_type = map;
170 map_type_len = path - map;
171 path += 1;
172 } else {
173 map_type = var_db_type;
174 map_type_len = strlen(map_type);
175 path = map;
176 }
177
178 /*
179 * Redirect the pathname.
180 */
181 vstring_strncpy(result, map_type, map_type_len);
182 if (name_code(data_redirect_map_types, NAME_CODE_FLAG_NONE, STR(result))) {
183 data_redirect_path(result, path, "table", map);
184 } else {
185 vstring_strcpy(result, path);
186 }
187
188 /*
189 * (Re)combine the map type with the map name.
190 */
191 vstring_prepend(result, MAP_DELIMITER, sizeof(MAP_DELIMITER) - 1);
192 vstring_prepend(result, map_type, map_type_len);
193 return (STR(result));
194 }
195
196 /*
197 * Proof-of-concept test program. This can't be run as automated regression
198 * test, because the result depends on main.cf information (mail_owner UID
199 * and data_directory pathname) and on local file system details.
200 */
201 #ifdef TEST
202
203 #include <unistd.h>
204 #include <stdlib.h>
205 #include <vstring_vstream.h>
206 #include <mail_conf.h>
207
main(int argc,char ** argv)208 int main(int argc, char **argv)
209 {
210 VSTRING *inbuf = vstring_alloc(100);
211 VSTRING *result = vstring_alloc(100);
212 char *bufp;
213 char *cmd;
214 char *target;
215 char *junk;
216
217 mail_conf_read();
218
219 while (vstring_get_nonl(inbuf, VSTREAM_IN) != VSTREAM_EOF) {
220 bufp = STR(inbuf);
221 if (!isatty(0)) {
222 vstream_printf("> %s\n", bufp);
223 vstream_fflush(VSTREAM_OUT);
224 }
225 if (*bufp == '#')
226 continue;
227 if ((cmd = mystrtok(&bufp, " \t")) == 0) {
228 vstream_printf("usage: file path|map maptype:mapname\n");
229 vstream_fflush(VSTREAM_OUT);
230 continue;
231 }
232 target = mystrtokq(&bufp, " \t");
233 junk = mystrtok(&bufp, " \t");
234 if (strcmp(cmd, "file") == 0 && target && !junk) {
235 data_redirect_file(result, target);
236 vstream_printf("%s -> %s\n", target, STR(result));
237 } else if (strcmp(cmd, "map") == 0 && target && !junk) {
238 data_redirect_map(result, target);
239 vstream_printf("%s -> %s\n", target, STR(result));
240 } else {
241 vstream_printf("usage: file path|map maptype:mapname\n");
242 }
243 vstream_fflush(VSTREAM_OUT);
244 }
245 vstring_free(inbuf);
246 return (0);
247 }
248
249 #endif
250