1 /* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
2  */
3 
4 #include "lib.h"
5 #include "time-util.h"
6 #include "istream.h"
7 
8 #include "sieve-ldap-storage.h"
9 
10 #if defined(SIEVE_BUILTIN_LDAP) || defined(PLUGIN_BUILD)
11 
12 #include "str.h"
13 #include "strfuncs.h"
14 
15 #include "sieve-error.h"
16 #include "sieve-dump.h"
17 #include "sieve-binary.h"
18 
19 /*
20  * Script file implementation
21  */
22 
sieve_ldap_script_alloc(void)23 static struct sieve_ldap_script *sieve_ldap_script_alloc(void)
24 {
25 	struct sieve_ldap_script *lscript;
26 	pool_t pool;
27 
28 	pool = pool_alloconly_create("sieve_ldap_script", 1024);
29 	lscript = p_new(pool, struct sieve_ldap_script, 1);
30 	lscript->script = sieve_ldap_script;
31 	lscript->script.pool = pool;
32 
33 	return lscript;
34 }
35 
sieve_ldap_script_init(struct sieve_ldap_storage * lstorage,const char * name)36 struct sieve_ldap_script *sieve_ldap_script_init
37 (struct sieve_ldap_storage *lstorage, const char *name)
38 {
39 	struct sieve_storage *storage = &lstorage->storage;
40 	struct sieve_ldap_script *lscript = NULL;
41 	const char *location;
42 
43 	if ( name == NULL ) {
44 		name = SIEVE_LDAP_SCRIPT_DEFAULT;
45 		location = storage->location;
46 	} else {
47 		location = t_strconcat
48 			(storage->location, ";name=", name, NULL);
49 	}
50 
51 	lscript = sieve_ldap_script_alloc();
52 	sieve_script_init(&lscript->script,
53 		storage, &sieve_ldap_script, location, name);
54 	return lscript;
55 }
56 
sieve_ldap_script_open(struct sieve_script * script,enum sieve_error * error_r)57 static int sieve_ldap_script_open
58 (struct sieve_script *script, enum sieve_error *error_r)
59 {
60 	struct sieve_ldap_script *lscript =
61 		(struct sieve_ldap_script *)script;
62 	struct sieve_storage *storage = script->storage;
63 	struct sieve_ldap_storage *lstorage =
64 		(struct sieve_ldap_storage *)storage;
65 	int ret;
66 
67 	if ( sieve_ldap_db_connect(lstorage->conn) < 0 ) {
68 		sieve_storage_set_critical(storage,
69 			"Failed to connect to LDAP database");
70 		*error_r = storage->error_code;
71 		return -1;
72 	}
73 
74 	if ( (ret=sieve_ldap_db_lookup_script(lstorage->conn,
75 		script->name, &lscript->dn, &lscript->modattr)) <= 0 ) {
76 		if ( ret == 0 ) {
77 			e_debug(script->event, "Script entry not found");
78 			sieve_script_set_error(script,
79 				SIEVE_ERROR_NOT_FOUND,
80 				"Sieve script not found");
81 		} else {
82 			sieve_script_set_internal_error(script);
83 		}
84 		*error_r = script->storage->error_code;
85 		return -1;
86 	}
87 
88 	return 0;
89 }
90 
sieve_ldap_script_get_stream(struct sieve_script * script,struct istream ** stream_r,enum sieve_error * error_r)91 static int sieve_ldap_script_get_stream
92 (struct sieve_script *script, struct istream **stream_r,
93 	enum sieve_error *error_r)
94 {
95 	struct sieve_ldap_script *lscript =
96 		(struct sieve_ldap_script *)script;
97 	struct sieve_storage *storage = script->storage;
98 	struct sieve_ldap_storage *lstorage =
99 		(struct sieve_ldap_storage *)storage;
100 	int ret;
101 
102 	i_assert(lscript->dn != NULL);
103 
104 	if ( (ret=sieve_ldap_db_read_script(
105 		lstorage->conn, lscript->dn, stream_r)) <= 0 ) {
106 		if ( ret == 0 ) {
107 			e_debug(script->event, "Script attribute not found");
108 			sieve_script_set_error(script,
109 				SIEVE_ERROR_NOT_FOUND,
110 				"Sieve script not found");
111 		} else {
112 			sieve_script_set_internal_error(script);
113 		}
114 		*error_r = script->storage->error_code;
115 		return -1;
116 	}
117 	return 0;
118 }
119 
sieve_ldap_script_binary_read_metadata(struct sieve_script * script,struct sieve_binary_block * sblock,sieve_size_t * offset)120 static int sieve_ldap_script_binary_read_metadata
121 (struct sieve_script *script, struct sieve_binary_block *sblock,
122 	sieve_size_t *offset)
123 {
124 	struct sieve_ldap_script *lscript =
125 		(struct sieve_ldap_script *)script;
126 	struct sieve_instance *svinst = script->storage->svinst;
127 	struct sieve_ldap_storage *lstorage =
128 		(struct sieve_ldap_storage *)script->storage;
129 	struct sieve_binary *sbin =
130 		sieve_binary_block_get_binary(sblock);
131 	time_t bmtime = sieve_binary_mtime(sbin);
132 	string_t *dn, *modattr;
133 
134 	/* config file changed? */
135 	if ( bmtime <= lstorage->set_mtime ) {
136 		if ( svinst->debug ) {
137 			e_debug(script->event,
138 				"Sieve binary `%s' is not newer "
139 				"than the LDAP configuration `%s' (%s <= %s)",
140 				sieve_binary_path(sbin), lstorage->config_file,
141 				t_strflocaltime("%Y-%m-%d %H:%M:%S", bmtime),
142 				t_strflocaltime("%Y-%m-%d %H:%M:%S", lstorage->set_mtime));
143 		}
144 		return 0;
145 	}
146 
147 	/* open script if not open already */
148 	if ( lscript->dn == NULL &&
149 		sieve_script_open(script, NULL) < 0 )
150 		return 0;
151 
152 	/* if modattr not found, recompile always */
153 	if ( lscript->modattr == NULL || *lscript->modattr == '\0' ) {
154 		e_error(script->event,
155 			"LDAP entry for script `%s' "
156 			"has no modified attribute `%s'",
157 			sieve_script_location(script),
158 			lstorage->set.sieve_ldap_mod_attr);
159 		return 0;
160 	}
161 
162 	/* compare DN in binary and from search result */
163 	if ( !sieve_binary_read_string(sblock, offset, &dn) ) {
164 		e_error(script->event,
165 			"Binary `%s' has invalid metadata for script `%s': "
166 			"Invalid DN",
167 			sieve_binary_path(sbin), sieve_script_location(script));
168 		return -1;
169 	}
170 	i_assert( lscript->dn != NULL );
171 	if ( strcmp(str_c(dn), lscript->dn) != 0 ) {
172 		e_debug(script->event,
173 			"Binary `%s' reports different LDAP DN for script `%s' "
174 			"(`%s' rather than `%s')",
175 			sieve_binary_path(sbin), sieve_script_location(script),
176 			str_c(dn), lscript->dn);
177 		return 0;
178 	}
179 
180 	/* compare modattr in binary and from search result */
181 	if ( !sieve_binary_read_string(sblock, offset, &modattr) ) {
182 		e_error(script->event,
183 			"Binary `%s' has invalid metadata for script `%s': "
184 			"Invalid modified attribute",
185 			sieve_binary_path(sbin), sieve_script_location(script));
186 		return -1;
187 	}
188 	if ( strcmp(str_c(modattr), lscript->modattr) != 0 ) {
189 		e_debug(script->event,
190 			"Binary `%s' reports different modified attribute content "
191 			"for script `%s' (`%s' rather than `%s')",
192 			sieve_binary_path(sbin), sieve_script_location(script),
193 			str_c(modattr), lscript->modattr);
194 		return 0;
195 	}
196 	return 1;
197 }
198 
sieve_ldap_script_binary_write_metadata(struct sieve_script * script,struct sieve_binary_block * sblock)199 static void sieve_ldap_script_binary_write_metadata
200 (struct sieve_script *script, struct sieve_binary_block *sblock)
201 {
202 	struct sieve_ldap_script *lscript =
203 		(struct sieve_ldap_script *)script;
204 
205 	sieve_binary_emit_cstring(sblock, lscript->dn);
206 	if (lscript->modattr == NULL)
207 		sieve_binary_emit_cstring(sblock, "");
208 	else
209 		sieve_binary_emit_cstring(sblock, lscript->modattr);
210 }
211 
sieve_ldap_script_binary_dump_metadata(struct sieve_script * script ATTR_UNUSED,struct sieve_dumptime_env * denv,struct sieve_binary_block * sblock,sieve_size_t * offset)212 static bool sieve_ldap_script_binary_dump_metadata
213 (struct sieve_script *script ATTR_UNUSED, struct sieve_dumptime_env *denv,
214 	struct sieve_binary_block *sblock, sieve_size_t *offset)
215 {
216 	string_t *dn, *modattr;
217 
218 	if ( !sieve_binary_read_string(sblock, offset, &dn) )
219 		return FALSE;
220 	sieve_binary_dumpf(denv, "ldap.dn = %s\n", str_c(dn));
221 
222 	if ( !sieve_binary_read_string(sblock, offset, &modattr) )
223 		return FALSE;
224 	sieve_binary_dumpf(denv, "ldap.mod_attr = %s\n", str_c(modattr));
225 
226 	return TRUE;
227 }
228 
sieve_ldap_script_get_binpath(struct sieve_ldap_script * lscript)229 static const char *sieve_ldap_script_get_binpath
230 (struct sieve_ldap_script *lscript)
231 {
232 	struct sieve_script *script = &lscript->script;
233 	struct sieve_storage *storage = script->storage;
234 
235 	if ( lscript->binpath == NULL ) {
236 		if ( storage->bin_dir == NULL )
237 			return NULL;
238 		lscript->binpath = p_strconcat(script->pool,
239 			storage->bin_dir, "/",
240 			sieve_binfile_from_name(script->name), NULL);
241 	}
242 
243 	return lscript->binpath;
244 }
245 
sieve_ldap_script_binary_load(struct sieve_script * script,enum sieve_error * error_r)246 static struct sieve_binary *sieve_ldap_script_binary_load
247 (struct sieve_script *script, enum sieve_error *error_r)
248 {
249 	struct sieve_storage *storage = script->storage;
250 	struct sieve_ldap_script *lscript =
251 		(struct sieve_ldap_script *)script;
252 
253 	if ( sieve_ldap_script_get_binpath(lscript) == NULL )
254 		return NULL;
255 
256 	return sieve_binary_open(storage->svinst,
257 		lscript->binpath, script, error_r);
258 }
259 
sieve_ldap_script_binary_save(struct sieve_script * script,struct sieve_binary * sbin,bool update,enum sieve_error * error_r)260 static int sieve_ldap_script_binary_save
261 (struct sieve_script *script, struct sieve_binary *sbin, bool update,
262 	enum sieve_error *error_r)
263 {
264 	struct sieve_ldap_script *lscript =
265 		(struct sieve_ldap_script *)script;
266 
267 	if ( sieve_ldap_script_get_binpath(lscript) == NULL )
268 		return 0;
269 
270 	if ( sieve_storage_setup_bindir(script->storage, 0700) < 0 )
271 		return -1;
272 
273 	return sieve_binary_save
274 		(sbin, lscript->binpath, update, 0600, error_r);
275 }
276 
sieve_ldap_script_equals(const struct sieve_script * script,const struct sieve_script * other)277 static bool sieve_ldap_script_equals
278 (const struct sieve_script *script, const struct sieve_script *other)
279 {
280 	struct sieve_storage *storage = script->storage;
281 	struct sieve_storage *sother = other->storage;
282 
283 	if ( strcmp(storage->location, sother->location) != 0 )
284 		return FALSE;
285 
286 	i_assert( script->name != NULL && other->name != NULL );
287 
288 	return ( strcmp(script->name, other->name) == 0 );
289 }
290 
291 const struct sieve_script sieve_ldap_script = {
292 	.driver_name = SIEVE_LDAP_STORAGE_DRIVER_NAME,
293 	.v = {
294 		.open = sieve_ldap_script_open,
295 
296 		.get_stream = sieve_ldap_script_get_stream,
297 
298 		.binary_read_metadata = sieve_ldap_script_binary_read_metadata,
299 		.binary_write_metadata = sieve_ldap_script_binary_write_metadata,
300 		.binary_dump_metadata = sieve_ldap_script_binary_dump_metadata,
301 		.binary_load = sieve_ldap_script_binary_load,
302 		.binary_save = sieve_ldap_script_binary_save,
303 
304 		.equals = sieve_ldap_script_equals
305 	}
306 };
307 
308 /*
309  * Script sequence
310  */
311 
312 struct sieve_ldap_script_sequence {
313 	struct sieve_script_sequence seq;
314 
315 	bool done:1;
316 };
317 
sieve_ldap_storage_get_script_sequence(struct sieve_storage * storage,enum sieve_error * error_r)318 struct sieve_script_sequence *sieve_ldap_storage_get_script_sequence
319 (struct sieve_storage *storage, enum sieve_error *error_r)
320 {
321 	struct sieve_ldap_script_sequence *lsec = NULL;
322 
323 	if ( error_r != NULL )
324 		*error_r = SIEVE_ERROR_NONE;
325 
326 	/* Create sequence object */
327 	lsec = i_new(struct sieve_ldap_script_sequence, 1);
328 	sieve_script_sequence_init(&lsec->seq, storage);
329 
330 	return &lsec->seq;
331 }
332 
sieve_ldap_script_sequence_next(struct sieve_script_sequence * seq,enum sieve_error * error_r)333 struct sieve_script *sieve_ldap_script_sequence_next
334 (struct sieve_script_sequence *seq, enum sieve_error *error_r)
335 {
336 	struct sieve_ldap_script_sequence *lsec =
337 		(struct sieve_ldap_script_sequence *)seq;
338 	struct sieve_ldap_storage *lstorage =
339 		(struct sieve_ldap_storage *)seq->storage;
340 	struct sieve_ldap_script *lscript;
341 
342 	if ( error_r != NULL )
343 		*error_r = SIEVE_ERROR_NONE;
344 
345 	if ( lsec->done )
346 		return NULL;
347 	lsec->done = TRUE;
348 
349 	lscript = sieve_ldap_script_init
350 		(lstorage, seq->storage->script_name);
351 	if ( sieve_script_open(&lscript->script, error_r) < 0 ) {
352 		struct sieve_script *script = &lscript->script;
353 		sieve_script_unref(&script);
354 		return NULL;
355 	}
356 
357 	return &lscript->script;
358 }
359 
sieve_ldap_script_sequence_destroy(struct sieve_script_sequence * seq)360 void sieve_ldap_script_sequence_destroy
361 (struct sieve_script_sequence *seq)
362 {
363 	struct sieve_ldap_script_sequence *lsec =
364 		(struct sieve_ldap_script_sequence *)seq;
365 	i_free(lsec);
366 }
367 
368 
369 #endif
370