Example #1
0
static void get_metadata_precache_fields(struct mailbox *box,
					 struct mailbox_metadata *metadata_r)
{
	const struct mail_cache_field *fields;
	unsigned int i, count;
	enum mail_fetch_field cache = 0;

	fields = mail_cache_register_get_list(box->cache,
					      pool_datastack_create(), &count);
	for (i = 0; i < count; i++) {
		const char *name = fields[i].name;

		if (strncmp(name, "hdr.", 4) == 0 ||
		    strcmp(name, "date.sent") == 0 ||
		    strcmp(name, "imap.envelope") == 0)
			cache |= MAIL_FETCH_STREAM_HEADER;
		else if (strcmp(name, "mime.parts") == 0 ||
			 strcmp(name, "imap.body") == 0 ||
			 strcmp(name, "imap.bodystructure") == 0)
			cache |= MAIL_FETCH_STREAM_BODY;
		else if (strcmp(name, "date.received") == 0)
			cache |= MAIL_FETCH_RECEIVED_DATE;
		else if (strcmp(name, "date.save") == 0)
			cache |= MAIL_FETCH_SAVE_DATE;
		else if (strcmp(name, "size.virtual") == 0)
			cache |= MAIL_FETCH_VIRTUAL_SIZE;
		else if (strcmp(name, "size.physical") == 0)
			cache |= MAIL_FETCH_PHYSICAL_SIZE;
		else if (strcmp(name, "pop3.uidl") == 0)
			cache |= MAIL_FETCH_UIDL_BACKEND;
		else if (strcmp(name, "guid") == 0)
			cache |= MAIL_FETCH_GUID;
		else if (strcmp(name, "flags") == 0) {
			/* just ignore for now at least.. */
		} else if (box->storage->set->mail_debug)
			i_debug("Ignoring unknown cache field: %s", name);
	}
	metadata_r->precache_fields = cache;
}
Example #2
0
static void fs_crypt_write_stream(struct fs_file *_file)
{
	struct crypt_fs_file *file = (struct crypt_fs_file *)_file;
	const char *error;

	i_assert(_file->output == NULL);

	if (fs_crypt_load_keys(file->fs, &error) < 0) {
		_file->output = o_stream_create_error_str(EIO,
			"Couldn't read settings: %s", error);
		return;
	}

	if (file->fs->keys.public_key == NULL) {
		if (_file->fs->set.debug)
			i_debug("No public key provided, "
				"NOT encrypting stream %s",
				 fs_file_path(_file));
		file->super_output = fs_write_stream(_file->parent);
		_file->output = file->super_output;
		return;
	}

	enum io_stream_encrypt_flags flags;
	if (strstr(file->fs->enc_algo, "gcm") != NULL ||
	    strstr(file->fs->enc_algo, "ccm") != NULL) {
		flags = IO_STREAM_ENC_INTEGRITY_AEAD;
	} else {
		flags = IO_STREAM_ENC_INTEGRITY_HMAC;
	}

	file->temp_output =
		iostream_temp_create_named(_file->fs->temp_path_prefix,
					   IOSTREAM_TEMP_FLAG_TRY_FD_DUP,
					   fs_file_path(_file));
	_file->output = o_stream_create_encrypt(file->temp_output,
		file->fs->enc_algo, file->fs->keys.public_key,
		flags);
}
static int
service_auth_userdb_lookup(struct mail_storage_service_ctx *ctx,
			   const struct mail_storage_service_input *input,
			   pool_t pool, const char **user,
			   const char *const **fields_r,
			   const char **error_r)
{
	struct auth_user_info info;
	const char *new_username;
	int ret;

	memset(&info, 0, sizeof(info));
	info.service = input->service != NULL ? input->service :
		ctx->service->name;
	info.local_ip = input->local_ip;
	info.remote_ip = input->remote_ip;
	info.local_port = input->local_port;
	info.remote_port = input->remote_port;
	info.debug = input->debug;

	ret = auth_master_user_lookup(ctx->conn, *user, &info, pool,
				      &new_username, fields_r);
	if (ret > 0) {
		if (strcmp(*user, new_username) != 0) {
			if (ctx->debug)
				i_debug("changed username to %s", new_username);
			*user = t_strdup(new_username);
		}
		*user = new_username;
	} else if (ret == 0)
		*error_r = "Unknown user";
	else if (**fields_r != NULL) {
		*error_r = t_strdup(**fields_r);
		ret = -2;
	} else {
		*error_r = MAIL_ERRSTR_CRITICAL_MSG;
	}
	return ret;
}
static void set_keyval(struct mail_storage_service_ctx *ctx,
		       struct mail_storage_service_user *user,
		       const char *key, const char *value)
{
	struct setting_parser_context *set_parser = user->set_parser;
	const char *str;

	if (master_service_set_has_config_override(ctx->service, key)) {
		/* this setting was already overridden with -o parameter */
		if (mail_user_set_get_mail_debug(user->user_info,
						 user->user_set)) {
			i_debug("Ignoring overridden (-o) userdb setting: %s",
				key);
		}
		return;
	}

	str = t_strconcat(key, "=", value, NULL);
	if (settings_parse_line(set_parser, str) < 0) {
		i_fatal("Invalid userdb input '%s': %s", str,
			settings_parser_get_error(set_parser));
	}
}
Example #5
0
static void
fts_tika_parser_response(const struct http_response *response,
			 struct tika_fts_parser *parser)
{
	i_assert(parser->payload == NULL);

	switch (response->status) {
	case 200:
		/* read response */
		if (response->payload == NULL)
			parser->payload = i_stream_create_from_data("", 0);
		else {
			i_stream_ref(response->payload);
			parser->payload = response->payload;
		}
		break;
	case 204: /* empty response */
	case 415: /* Unsupported Media Type */
	case 422: /* Unprocessable Entity */
		if (parser->user->mail_debug) {
			i_debug("fts_tika: PUT %s failed: %u %s",
				mail_user_plugin_getenv(parser->user, "fts_tika"),
				response->status, response->reason);
		}
		parser->payload = i_stream_create_from_data("", 0);
		break;
	default:
		i_error("fts_tika: PUT %s failed: %u %s",
			mail_user_plugin_getenv(parser->user, "fts_tika"),
			response->status, response->reason);
		parser->failed = TRUE;
		break;
	}
	parser->http_req = NULL;
	io_loop_stop(current_ioloop);
}
Example #6
0
static bool mbox_storage_autodetect(const struct mail_namespace *ns,
				    struct mailbox_list_settings *set)
{
	bool debug = ns->mail_set->mail_debug;
	const char *root_dir, *inbox_path;

	root_dir = set->root_dir;
	inbox_path = set->inbox_path;

	if (root_dir != NULL) {
		if (inbox_path == NULL &&
		    mbox_is_file(root_dir, "INBOX file", debug)) {
			/* using location=<INBOX> */
			inbox_path = root_dir;
			root_dir = NULL;
		} else if (!mbox_storage_is_root_dir(root_dir, debug))
			return FALSE;
	}
	if (root_dir == NULL) {
		root_dir = mbox_storage_find_root_dir(ns);
		if (root_dir == NULL) {
			if (debug)
				i_debug("mbox: couldn't find root dir");
			return FALSE;
		}
	}
	if (inbox_path == NULL) {
		inbox_path = mbox_storage_find_inbox_file(ns->user->username,
							  debug);
	}
	set->root_dir = root_dir;
	set->inbox_path = inbox_path;

	mbox_storage_get_list_settings(ns, set);
	return TRUE;
}
Example #7
0
static int
sieve_attribute_iter_script_init(struct sieve_mailbox_attribute_iter *siter)
{
	struct mail_user *user = siter->iter.box->storage->user;
	struct sieve_storage *svstorage;
	int ret;

	if (user->mail_debug)
		i_debug("doveadm-sieve: Iterating Sieve mailbox attributes");

	if ((ret = mail_sieve_user_init(user, &svstorage)) <= 0)
		return ret;

	siter->sieve_list = sieve_storage_list_init(svstorage);
	if (siter->sieve_list == NULL) {
		mail_storage_set_critical(siter->iter.box->storage,
			"Failed to iterate sieve scripts: %s",
			sieve_storage_get_last_error(svstorage, NULL));
		return -1;
	}
	siter->name = str_new(default_pool, 128);
	str_append(siter->name, MAILBOX_ATTRIBUTE_PREFIX_SIEVE_FILES);
	return 0;
}
Example #8
0
static int
cmd_altmove_box(struct doveadm_mail_cmd_context *ctx,
		const struct mailbox_info *info,
		struct mail_search_args *search_args, bool reverse)
{
	struct doveadm_mail_iter *iter;
	struct mail *mail;
	enum modify_type modify_type =
		!reverse ? MODIFY_ADD : MODIFY_REMOVE;

	if (doveadm_mail_iter_init(ctx, info, search_args, 0, NULL, FALSE,
				   &iter) < 0)
		return -1;

	while (doveadm_mail_iter_next(iter, &mail)) {
		if (doveadm_debug) {
			i_debug("altmove: box=%s uid=%u",
				info->vname, mail->uid);
		}
		mail_update_flags(mail, modify_type,
			(enum mail_flags)MAIL_INDEX_MAIL_FLAG_BACKEND);
	}
	return doveadm_mail_iter_deinit_sync(&iter);
}
bool dsync_brain_slave_recv_mailbox(struct dsync_brain *brain)
{
	const struct dsync_mailbox *dsync_box;
	struct dsync_mailbox local_dsync_box;
	struct mailbox *box;
	const char *error;
	int ret;

	i_assert(!brain->master_brain);
	i_assert(brain->box == NULL);

	if ((ret = dsync_ibc_recv_mailbox(brain->ibc, &dsync_box)) == 0)
		return FALSE;
	if (ret < 0) {
		brain->state = DSYNC_STATE_DONE;
		return TRUE;
	}

	if (dsync_brain_mailbox_alloc(brain, dsync_box->mailbox_guid,
				      &box, &error) < 0) {
		i_error("Couldn't allocate mailbox GUID %s: %s",
			guid_128_to_string(dsync_box->mailbox_guid), error);
		i_assert(brain->failed);
		return TRUE;
	}
	if (box == NULL) {
		/* mailbox was probably deleted/renamed during sync */
		if (brain->backup_send && brain->no_backup_overwrite) {
			if (brain->debug) {
				i_debug("brain %c: Ignore nonexistent "
					"mailbox GUID %s with -1 sync",
					brain->master_brain ? 'M' : 'S',
					guid_128_to_string(dsync_box->mailbox_guid));
			}
			dsync_brain_slave_send_mailbox_lost(brain, dsync_box);
			return TRUE;
		}
		//FIXME: verify this from log, and if not log an error.
		if (brain->debug) {
			i_debug("brain %c: Change during sync: "
				"Mailbox GUID %s was lost",
				brain->master_brain ? 'M' : 'S',
				guid_128_to_string(dsync_box->mailbox_guid));
		}
		brain->changes_during_sync = TRUE;
		dsync_brain_slave_send_mailbox_lost(brain, dsync_box);
		return TRUE;
	}
	if (mailbox_sync(box, MAILBOX_SYNC_FLAG_FULL_READ) < 0) {
		i_error("Can't sync mailbox %s: %s",
			mailbox_get_vname(box),
			mailbox_get_last_error(box, NULL));
		mailbox_free(&box);
		brain->failed = TRUE;
		return TRUE;
	}

	if ((ret = dsync_box_get(box, &local_dsync_box)) <= 0) {
		mailbox_free(&box);
		if (ret < 0) {
			brain->failed = TRUE;
			return TRUE;
		}
		/* another process just deleted this mailbox? */
		dsync_brain_slave_send_mailbox_lost(brain, dsync_box);
		return TRUE;
	}
	i_assert(local_dsync_box.uid_validity != 0);
	i_assert(memcmp(dsync_box->mailbox_guid, local_dsync_box.mailbox_guid,
			sizeof(dsync_box->mailbox_guid)) == 0);
	dsync_ibc_send_mailbox(brain->ibc, &local_dsync_box);

	dsync_brain_mailbox_update_pre(brain, box, &local_dsync_box, dsync_box);

	if (!dsync_boxes_need_sync(brain, &local_dsync_box, dsync_box)) {
		/* no fields appear to have changed, skip this mailbox */
		mailbox_free(&box);
		return TRUE;
	}

	/* start export/import */
	dsync_brain_sync_mailbox_init(brain, box, &local_dsync_box, FALSE);
	if (dsync_brain_sync_mailbox_open(brain, dsync_box) < 0)
		return TRUE;

	brain->state = DSYNC_STATE_SYNC_MAILS;
	return TRUE;
}
static int
imap_hibernate_client_input_line(struct connection *conn, const char *line)
{
	struct imap_hibernate_client *client =
		(struct imap_hibernate_client *)conn;
	int fd = -1, ret;

	if (!conn->version_received) {
		if (connection_verify_version(conn, t_strsplit_tabescaped(line)) < 0)
			return -1;
		conn->version_received = TRUE;
		return 1;
	}

	if (client->imap_client == NULL) {
		char *const *args;
		pool_t pool;

		fd = i_stream_unix_get_read_fd(conn->input);
		if (fd == -1) {
			i_error("IMAP client fd not received");
			return -1;
		}

		pool = pool_alloconly_create("client cmd", 1024);
		args = p_strsplit_tabescaped(pool, line);
		ret = imap_hibernate_client_input_args(conn, (void *)args, fd, pool);
		if (ret >= 0 && client->debug)
			i_debug("Create client with input: %s", line);
		pool_unref(&pool);
	} else {
		fd = i_stream_unix_get_read_fd(conn->input);
		if (fd == -1) {
			i_error("IMAP notify fd not received (input: %s)", line);
			ret = -1;
		} else if (line[0] != '\0') {
			i_error("Expected empty notify fd line from client, but got: %s", line);
			o_stream_send_str(conn->output,
					  "Expected empty notify fd line");
			ret = -1;
		} else {
			imap_client_add_notify_fd(client->imap_client, fd);
			ret = 1;
		}
	}

	if (ret < 0) {
		if (client->imap_client != NULL)
			imap_client_destroy(&client->imap_client, NULL);
		if (fd != -1)
			i_close_fd(&fd);
		return -1;
	} else if (ret == 0) {
		/* still need to read another fd */
		i_stream_unix_set_read_fd(conn->input);
		o_stream_send_str(conn->output, "+\n");
		return 1;
	} else {
		/* finished - always disconnect the hibernate client
		   afterwards */
		o_stream_send_str(conn->output, "+\n");
		imap_client_create_finish(client->imap_client);
		return -1;
	}
}
Example #11
0
static int
index_mailbox(struct master_connection *conn, struct mail_user *user,
	      const char *mailbox, unsigned int max_recent_msgs,
	      const char *what)
{
	struct mail_namespace *ns;
	struct mailbox *box;
	struct mailbox_status status;
	const char *path, *errstr;
	enum mail_error error;
	enum mailbox_sync_flags sync_flags = MAILBOX_SYNC_FLAG_FULL_READ;
	int ret;

	ns = mail_namespace_find(user->namespaces, mailbox);
	box = mailbox_alloc(ns->list, mailbox, 0);
	mailbox_set_reason(box, "indexing");
	ret = mailbox_get_path_to(box, MAILBOX_LIST_PATH_TYPE_INDEX, &path);
	if (ret < 0) {
		i_error("Getting path to mailbox %s failed: %s",
			mailbox, mailbox_get_last_internal_error(box, NULL));
		mailbox_free(&box);
		return -1;
	}
	if (ret == 0) {
		i_info("Indexes disabled for mailbox %s, skipping", mailbox);
		mailbox_free(&box);
		return 0;
	}
	ret = 0;

	if (max_recent_msgs != 0) {
		/* index only if there aren't too many recent messages.
		   don't bother syncing the mailbox, that alone can take a
		   while with large maildirs. */
		if (mailbox_open(box) < 0) {
			i_error("Opening mailbox %s failed: %s", mailbox,
				mailbox_get_last_internal_error(box, NULL));
			ret = -1;
		} else {
			mailbox_get_open_status(box, STATUS_RECENT, &status);
		}
		if (ret < 0 || status.recent > max_recent_msgs) {
			mailbox_free(&box);
			return ret;
		}
	}

	if (strchr(what, 'o') != NULL)
		sync_flags |= MAILBOX_SYNC_FLAG_OPTIMIZE;

	if (mailbox_sync(box, sync_flags) < 0) {
		errstr = mailbox_get_last_internal_error(box, &error);
		if (error != MAIL_ERROR_NOTFOUND) {
			i_error("Syncing mailbox %s failed: %s",
				mailbox, errstr);
		} else if (user->mail_debug) {
			i_debug("Syncing mailbox %s failed: %s",
				mailbox, errstr);
		}
		ret = -1;
	} else if (strchr(what, 'i') != NULL) {
		if (index_mailbox_precache(conn, box) < 0)
			ret = -1;
	}
	mailbox_free(&box);
	return ret;
}
Example #12
0
static void client_ctrl_input(struct client *client)
{
	const char *const *args;
	const char *line;
	int ret;

	timeout_reset(client->to_idle);

	if (client->fd_in == -1 || client->fd_out == -1) {
		if ((ret = client_ctrl_read_fds(client)) <= 0) {
			if (ret < 0)
				client_abort(client, "FD Transfer failed");
			return;
		}
	}

	switch (i_stream_read(client->ctrl_input)) {
	case -1:
		/* disconnected */
		client_destroy(client);
		return;
	case -2:
		/* line too long, kill it */
		client_abort(client,
			     "Control session aborted: Input line too long");
		return;
	}

	if (!client->version_received) {
		if ((line = i_stream_next_line(client->ctrl_input)) == NULL)
			return;

		if (!version_string_verify(line, "imap-urlauth-worker",
				IMAP_URLAUTH_WORKER_PROTOCOL_MAJOR_VERSION)) {
			i_error("imap-urlauth-worker client not compatible with this server "
				"(mixed old and new binaries?) %s", line);
			client_abort(client, "Control session aborted: Version mismatch");
			return;
		}

		client->version_received = TRUE;
		if (o_stream_send_str(client->ctrl_output, "OK\n") < 0) {
			client_destroy(client);
			return;
		}
	}

	if (client->access_received) {
		client_abort(client, "Control session aborted: Unexpected input");
		return;
	}

	if ((line = i_stream_next_line(client->ctrl_input)) == NULL)
		return;

	args = t_strsplit_tabescaped(line);
	if (*args == NULL || strcmp(*args, "ACCESS") != 0) {
		i_error("Invalid control command: %s", str_sanitize(line, 80));
		client_abort(client, "Control session aborted: Invalid command");
		return;
	}
	args++;
	if (*args == NULL) {
		i_error("Invalid ACCESS command: %s", str_sanitize(line, 80));
		client_abort(client, "Control session aborted: Invalid command");
		return;
	}

	i_assert(client->access_user == NULL);
	if (**args != '\0') {
		client->access_user = i_strdup(*args);
		client->access_anonymous = FALSE;
	} else {
		client->access_user = i_strdup("anonymous");
		client->access_anonymous = TRUE;
	}
	i_set_failure_prefix("imap-urlauth[%s](%s): ",
			     my_pid, client->access_user);

	args++;
	while (*args != NULL) {
		/* debug */
		if (strcasecmp(*args, "debug") == 0) {
			client->debug = TRUE;
		/* apps=<access-application>[,<access-application,...] */
		} else if (strncasecmp(*args, "apps=", 5) == 0 &&
			   (*args)[5] != '\0') {
			const char *const *apps = t_strsplit(*args+5, ",");

			while (*apps != NULL) {
				char *app = i_strdup(*apps);

				array_append(&client->access_apps, &app, 1);
				if (client->debug) {
					i_debug("User %s has URLAUTH %s access",
						client->access_user, app);
				}
				apps++;
			}
		} else {
			i_error("Invalid ACCESS parameter: %s", str_sanitize(*args, 80));
			client_abort(client, "Control session aborted: Invalid command");
			return;
		} 
		args++;
	}

	client->access_received = TRUE;

	if (o_stream_send_str(client->ctrl_output, "OK\n") < 0) {
		client_destroy(client);
		return;
	}

	client->input = i_stream_create_fd(client->fd_in, MAX_INBUF_SIZE, FALSE);
	client->output = o_stream_create_fd(client->fd_out, (size_t)-1, FALSE); 
	client->io = io_add(client->fd_in, IO_READ, client_input, client);
	o_stream_set_flush_callback(client->output, client_output, client);

	if (client->debug) {
		i_debug("Worker activated for access by user %s",
			client->access_user);
	}
}
Example #13
0
static int
imap_master_client_input_args(struct connection *conn, const char *const *args,
			      int fd_client, pool_t pool)
{
	struct imap_master_client *client = (struct imap_master_client *)conn;
	struct client *imap_client;
	struct mail_storage_service_input input;
	struct imap_master_input master_input;
	const char *error;
	int ret;

	if (imap_master_client_parse_input(args, pool, &input, &master_input,
					   &error) < 0) {
		i_error("imap-master: Failed to parse client input: %s", error);
		o_stream_nsend_str(conn->output, t_strdup_printf(
			"-Failed to parse client input: %s\n", error));
		i_close_fd(&fd_client);
		return -1;
	}
	if (imap_master_client_verify(&master_input, fd_client, &error) < 0) {
		i_error("imap-master: Failed to verify client input: %s", error);
		o_stream_nsend_str(conn->output, t_strdup_printf(
			"-Failed to verify client input: %s\n", error));
		i_close_fd(&fd_client);
		return -1;
	}
	/* Send a success notification before we start anything that lasts
	   potentially a long time. imap-hibernate process is waiting for us
	   to answer. Even if we fail later, we log the error anyway. */
	o_stream_nsend_str(conn->output, "+\n");
	(void)o_stream_flush(conn->output);

	/* NOTE: before client_create_from_input() on failures we need to close
	   fd_client, but afterward it gets closed by client_destroy() */
	ret = client_create_from_input(&input, fd_client, fd_client,
				       &imap_client, &error);
	if (ret < 0) {
		i_error("imap-master(%s): Failed to create client: %s",
			input.username, error);
		i_close_fd(&fd_client);
		return -1;
	}
	client->imap_client_created = TRUE;

	if (client_create_finish(imap_client, &error) < 0) {
		i_error("imap-master(%s): %s", input.username, error);
		client_destroy(imap_client, error);
		return -1;
	}
	/* log prefix is set at this point, so we don't need to add the
	   username anymore to the log messages */

	o_stream_nsend(imap_client->output,
		       master_input.client_output->data,
		       master_input.client_output->used);
	if (master_input.client_input->used > 0 &&
	    !i_stream_add_data(imap_client->input,
			       master_input.client_input->data,
			       master_input.client_input->used)) {
		i_error("imap-master: Couldn't add %"PRIuSIZE_T
			" bytes to client's input stream",
			master_input.client_input->used);
		client_destroy(imap_client, "Client initialization failed");
		return -1;
	}
	imap_client->state_import_bad_idle_done =
		master_input.state_import_bad_idle_done;
	imap_client->state_import_idle_continue =
		master_input.state_import_idle_continue;
	if (imap_client->user->mail_debug) {
		if (imap_client->state_import_bad_idle_done)
			i_debug("imap-master: Unhibernated because IDLE was stopped with BAD command");
		else if (imap_client->state_import_idle_continue)
			i_debug("imap-master: Unhibernated to send mailbox changes");
		else
			i_debug("imap-master: Unhibernated because IDLE was stopped with DONE");
	}

	ret = imap_state_import_internal(imap_client, master_input.state->data,
					 master_input.state->used, &error);
	if (ret <= 0) {
		i_error("imap-master: Failed to import client state: %s", error);
		client_destroy(imap_client, "Client state initialization failed");
		return -1;
	}

	if (master_input.tag != NULL)
		imap_state_import_idle_cmd_tag(imap_client, master_input.tag);

	/* make sure all pending input gets handled */
	i_assert(imap_client->to_delayed_input == NULL);
	if (master_input.client_input->used > 0) {
		if (imap_client->user->mail_debug) {
			i_debug("imap-master: Pending client input: '%s'",
				str_sanitize(str_c(master_input.client_input), 128));
		}
		imap_client->to_delayed_input =
			timeout_add(0, client_input, imap_client);
	}

	imap_refresh_proctitle();
	/* we'll always disconnect the client afterwards */
	return -1;
}
static int program_client_local_disconnect
(struct program_client *pclient, bool force)
{
	struct program_client_local *slclient = 
		(struct program_client_local *) pclient;
	pid_t pid = slclient->pid, ret;
	time_t runtime, timeout = 0;
	int status;
	
	if ( pid < 0 ) {
		/* program never started */
		pclient->exit_code = 0;
		return 0;
	}

	slclient->pid = -1;

	/* Calculate timeout */
	runtime = ioloop_time - pclient->start_time;
	if ( !force && pclient->set.input_idle_timeout_secs > 0 &&
		runtime < (time_t)pclient->set.input_idle_timeout_secs )
		timeout = pclient->set.input_idle_timeout_secs - runtime;

	if ( pclient->debug ) {
		i_debug("waiting for program `%s' to finish after %llu seconds",
			pclient->path, (unsigned long long int)runtime);
	}

	/* Wait for child to exit */
	force = force ||
		(timeout == 0 && pclient->set.input_idle_timeout_secs > 0);
	if ( !force ) {
		alarm(timeout);
		ret = waitpid(pid, &status, 0);
		alarm(0);
	}
	if ( force || ret < 0 ) {
		if ( !force && errno != EINTR ) {
			i_error("waitpid(%s) failed: %m", pclient->path);
			(void)kill(pid, SIGKILL);
			return -1;
		}

		/* Timed out */
		force = TRUE;
		if ( pclient->error == PROGRAM_CLIENT_ERROR_NONE )
			pclient->error = PROGRAM_CLIENT_ERROR_RUN_TIMEOUT;
		if ( pclient->debug ) {
			i_debug("program `%s' execution timed out after %llu seconds: "
				"sending TERM signal", pclient->path,
				(unsigned long long int)pclient->set.input_idle_timeout_secs);
		}

		/* Kill child gently first */
		if ( kill(pid, SIGTERM) < 0 ) {
			i_error("failed to send SIGTERM signal to program `%s'", pclient->path);
			(void)kill(pid, SIGKILL);
			return -1;
		} 
			
		/* Wait for it to die (give it some more time) */
		alarm(5);
		ret = waitpid(pid, &status, 0);
		alarm(0);
		if ( ret < 0 ) {
			if ( errno != EINTR ) {
				i_error("waitpid(%s) failed: %m", pclient->path);
				(void)kill(pid, SIGKILL); 
				return -1;
			}

			/* Timed out again */
			if ( pclient->debug ) {
				i_debug("program `%s' execution timed out: sending KILL signal",
					pclient->path);
			}

			/* Kill it brutally now */
			if ( kill(pid, SIGKILL) < 0 ) {
				i_error("failed to send SIGKILL signal to program `%s'",
					pclient->path);
				return -1;
			}

			/* Now it will die immediately */
			if ( waitpid(pid, &status, 0) < 0 ) {
				i_error("waitpid(%s) failed: %m", pclient->path);
				return -1;
			}
		}
	}
	
	/* Evaluate child exit status */
	pclient->exit_code = -1;
	if ( WIFEXITED(status) ) {
		/* Exited */
		int exit_code = WEXITSTATUS(status);
				
		if ( exit_code != 0 ) {
			i_info("program `%s' terminated with non-zero exit code %d", 
				pclient->path, exit_code);
			pclient->exit_code = 0;
			return 0;
		}

		pclient->exit_code = 1;
		return 1;	

	} else if ( WIFSIGNALED(status) ) {
		/* Killed with a signal */
		
		if ( force ) {
			i_error("program `%s' was forcibly terminated with signal %d",
				pclient->path, WTERMSIG(status));
		} else {
			i_error("program `%s' terminated abnormally, signal %d",
				pclient->path, WTERMSIG(status));
		}
		return -1;

	} else if ( WIFSTOPPED(status) ) {
		/* Stopped */
		i_error("program `%s' stopped, signal %d",
			pclient->path, WSTOPSIG(status));
		return -1;
	} 

	/* Something else */
	i_error("program `%s' terminated abnormally, return status %d",
		pclient->path, status);
	return -1;
}
static void client_auth_parse_args(struct client *client,
				   const char *const *args,
				   struct client_auth_reply *reply_r)
{
	const char *key, *value, *p;

	memset(reply_r, 0, sizeof(*reply_r));

	for (; *args != NULL; args++) {
		p = strchr(*args, '=');
		if (p == NULL) {
			key = *args;
			value = "";
		} else {
			key = t_strdup_until(*args, p);
			value = p + 1;
		}
		if (strcmp(key, "nologin") == 0)
			reply_r->nologin = TRUE;
		else if (strcmp(key, "proxy") == 0)
			reply_r->proxy = TRUE;
		else if (strcmp(key, "temp") == 0)
			reply_r->temp = TRUE;
		else if (strcmp(key, "authz") == 0)
			reply_r->authz_failure = TRUE;
		else if (strcmp(key, "user_disabled") == 0)
			client->auth_user_disabled = TRUE;
		else if (strcmp(key, "pass_expired") == 0)
			client->auth_pass_expired = TRUE;
		else if (strcmp(key, "reason") == 0)
			reply_r->reason = value;
		else if (strcmp(key, "host") == 0)
			reply_r->host = value;
		else if (strcmp(key, "hostip") == 0)
			reply_r->hostip = value;
		else if (strcmp(key, "port") == 0)
			reply_r->port = atoi(value);
		else if (strcmp(key, "destuser") == 0)
			reply_r->destuser = value;
		else if (strcmp(key, "pass") == 0)
			reply_r->password = value;
		else if (strcmp(key, "proxy_timeout") == 0)
			reply_r->proxy_timeout_msecs = 1000*atoi(value);
		else if (strcmp(key, "proxy_refresh") == 0)
			reply_r->proxy_refresh_secs = atoi(value);
		else if (strcmp(key, "proxy_mech") == 0)
			reply_r->proxy_mech = value;
		else if (strcmp(key, "proxy_nopipelining") == 0)
			reply_r->proxy_nopipelining = TRUE;
		else if (strcmp(key, "master") == 0)
			reply_r->master_user = value;
		else if (strcmp(key, "ssl") == 0) {
			reply_r->ssl_flags |= PROXY_SSL_FLAG_YES;
			if (strcmp(value, "any-cert") == 0)
				reply_r->ssl_flags |= PROXY_SSL_FLAG_ANY_CERT;
			if (reply_r->port == 0)
				reply_r->port = login_binary->default_ssl_port;
		} else if (strcmp(key, "starttls") == 0) {
			reply_r->ssl_flags |= PROXY_SSL_FLAG_YES |
				PROXY_SSL_FLAG_STARTTLS;
			if (strcmp(value, "any-cert") == 0)
				reply_r->ssl_flags |= PROXY_SSL_FLAG_ANY_CERT;
		} else if (strcmp(key, "user") == 0) {
			/* already handled in login-common */
		} else if (client->set->auth_debug)
			i_debug("Ignoring unknown passdb extra field: %s", key);
	}
	if (reply_r->port == 0)
		reply_r->port = login_binary->default_port;

	if (reply_r->destuser == NULL)
		reply_r->destuser = client->virtual_user;
}
Example #16
0
static int
module_load(const char *path, const char *name,
	    const struct module_dir_load_settings *set,
	    struct module *all_modules,
	    struct module **module_r, const char **error_r)
{
	void *handle;
	struct module *module;
	const char *const *module_version;

	*module_r = NULL;
	*error_r = NULL;

	if (set->ignore_dlopen_errors) {
		handle = quiet_dlopen(path, RTLD_GLOBAL | RTLD_NOW);
		if (handle == NULL) {
			if (set->debug) {
				i_debug("Skipping module %s, "
					"because dlopen() failed: %s "
					"(this is usually intentional, "
					"so just ignore this message)",
					name, dlerror());
			}
			return 0;
		}
	} else {
		handle = dlopen(path, RTLD_GLOBAL | RTLD_NOW);
		if (handle == NULL) {
			*error_r = t_strdup_printf("dlopen() failed: %s",
						   dlerror());
#ifdef RTLD_LAZY
			/* try to give a better error message by lazily loading
			   the plugin and checking its dependencies */
			handle = dlopen(path, RTLD_LAZY);
			if (handle == NULL)
				return -1;
#else
			return -1;
#endif
		}
	}

	module = i_new(struct module, 1);
	module->path = i_strdup(path);
	module->name = i_strdup(name);
	module->handle = handle;

	module_version = set->abi_version == NULL ? NULL :
		get_symbol(module, t_strconcat(name, "_version", NULL), TRUE);
	if (module_version != NULL &&
	    !versions_equal(*module_version, set->abi_version)) {
		*error_r = t_strdup_printf(
			"Module is for different ABI version %s (we have %s)",
			*module_version, set->abi_version);
		module_free(module);
		return -1;
	}

	/* get our init func */
	module->init = (void (*)(struct module *))
		get_symbol(module, t_strconcat(name, "_init", NULL),
			   !set->require_init_funcs);
	module->deinit = (void (*)(void))
		get_symbol(module, t_strconcat(name, "_deinit", NULL),
			   !set->require_init_funcs);

	if ((module->init == NULL || module->deinit == NULL) &&
	    set->require_init_funcs) {
		*error_r = t_strdup_printf(
			"Module doesn't have %s function",
			module->init == NULL ? "init" : "deinit");
	} else if (!module_check_wrong_binary_dependency(set, module, error_r)) {
		/* failed */
	} else if (!module_check_missing_plugin_dependencies(set, module,
							     all_modules, error_r)) {
		/* failed */
	}

	if (*error_r != NULL) {
		module->deinit = NULL;
		module_free(module);
		return -1;
	}

	if (set->debug)
		i_debug("Module loaded: %s", path);
	*module_r = module;
	return 1;
}
static void
imap_urlauth_fetch_local(struct imap_urlauth_fetch *ufetch, const char *url,
			 enum imap_urlauth_fetch_flags url_flags,
			 struct imap_url *imap_url)
{
	struct imap_urlauth_fetch_reply reply;
	struct imap_msgpart_open_result mpresult;
	const char *error, *errormsg = NULL, *bpstruct = NULL;
	bool debug = ufetch->uctx->user->mail_debug, success;
	enum mail_error error_code;
	struct imap_msgpart_url *mpurl = NULL;
	int ret;

	success = TRUE;

	if (debug)
		i_debug("Fetching local URLAUTH %s", url);

	if (url_flags == 0)
		url_flags = IMAP_URLAUTH_FETCH_FLAG_BODY;

	/* fetch URL */
	if (imap_url == NULL) {
		ret = imap_urlauth_fetch(ufetch->uctx, url,
					 &mpurl, &error_code, &error);
	} else {
		ret = imap_urlauth_fetch_parsed(ufetch->uctx, imap_url,
						&mpurl, &error_code, &error);
	}
	if (ret <= 0) {
		if (ret == 0) {
			errormsg = t_strdup_printf("Failed to fetch URLAUTH \"%s\": %s",
						   url, error);
			if (debug)
				i_debug("%s", errormsg);
		}
		success = FALSE;
	}

	/* fetch metadata */
	if (success && (url_flags & IMAP_URLAUTH_FETCH_FLAG_BINARY) != 0)
		imap_msgpart_url_set_decode_to_binary(mpurl);
	if (success &&
	    (url_flags & IMAP_URLAUTH_FETCH_FLAG_BODYPARTSTRUCTURE) != 0) {
		ret = imap_msgpart_url_get_bodypartstructure(mpurl, &bpstruct, &error);
		if (ret <= 0) {
			if (ret == 0) {
				errormsg = t_strdup_printf
					("Failed to read URLAUTH \"%s\": %s",	url, error);
				if (debug)
					i_debug("%s", errormsg);
			}
			success = FALSE;
		}
	}

	/* if requested, read the message part the URL points to */
	memset(&mpresult, 0, sizeof(mpresult));
	if (success && ((url_flags & IMAP_URLAUTH_FETCH_FLAG_BODY) != 0 ||
			(url_flags & IMAP_URLAUTH_FETCH_FLAG_BINARY) != 0)) {
		ret = imap_msgpart_url_read_part(mpurl, &mpresult, &error);
		if (ret <= 0) {
			if (ret == 0) {
				errormsg = t_strdup_printf
					("Failed to read URLAUTH \"%s\": %s",	url, error);
				if (debug)
					i_debug("%s", errormsg);
			}
			success = FALSE;
		}
	}

	if (debug && success) {
		if (bpstruct != NULL)
			i_debug("Fetched URLAUTH yielded BODYPARTSTRUCTURE (%s)", bpstruct);
		if (mpresult.size == 0 || mpresult.input == NULL)
			i_debug("Fetched URLAUTH yielded empty result");
		else {
			i_debug("Fetched URLAUTH yielded %"PRIuUOFF_T" bytes "
				"of %smessage data", mpresult.size,
				(mpresult.binary_decoded_input_has_nuls ? "binary " : ""));
		}
	}

	ufetch->pending_requests--;

	if (!success && ret < 0) {
		if (mpurl != NULL)
			imap_msgpart_url_free(&mpurl);
		(void)ufetch->callback(NULL, TRUE, ufetch->context);
		imap_urlauth_fetch_fail(ufetch);
		return;
	}

	memset(&reply, 0, sizeof(reply));
	reply.url = url;
	reply.flags = url_flags;
	reply.error = errormsg;
	reply.succeeded = success;

	reply.bodypartstruct = bpstruct;
	reply.binary_has_nuls = mpresult.binary_decoded_input_has_nuls;
	reply.size = mpresult.size;
	reply.input = mpresult.input;

	ret = ufetch->callback(&reply, ufetch->pending_requests == 0,
			       ufetch->context);
	if (ret == 0) {
		ufetch->local_url = mpurl;
		ufetch->waiting_local = TRUE;
		ufetch->pending_requests++;
	} else {

		if (mpurl != NULL)
			imap_msgpart_url_free(&mpurl);
		if (ret < 0)
			imap_urlauth_fetch_fail(ufetch);
	}
}
static int
sync_create_box(struct dsync_brain *brain, struct mailbox *box,
		const guid_128_t mailbox_guid, uint32_t uid_validity,
		enum mail_error *error_r)
{
	struct mailbox_metadata metadata;
	struct mailbox_update update;
	enum mail_error error;
	const char *errstr;
	int ret;

	memset(&update, 0, sizeof(update));
	memcpy(update.mailbox_guid, mailbox_guid, sizeof(update.mailbox_guid));
	update.uid_validity = uid_validity;

	if (mailbox_create(box, &update, FALSE) < 0) {
		errstr = mailbox_get_last_error(box, &error);
		if (error != MAIL_ERROR_EXISTS) {
			i_error("Can't create mailbox %s: %s",
				mailbox_get_vname(box), errstr);
			*error_r = error;
			return -1;
		}
	}
	if (brain->no_mail_sync) {
		/* trust that create worked, we can't actually open it
		   and verify. */
		return 0;
	}
	/* sync the mailbox so we can look up its latest status */
	if (mailbox_sync(box, MAILBOX_SYNC_FLAG_FULL_READ) < 0) {
		i_error("Can't sync mailbox %s: %s",
			mailbox_get_vname(box),
			mailbox_get_last_error(box, error_r));
		return -1;
	}

	/* verify that the GUID is what we wanted. if it's not, it probably
	   means that the mailbox had already been created. then we'll use the
	   GUID that is higher.

	   mismatching UIDVALIDITY is handled later, because we choose it by
	   checking which mailbox has more messages */
	if (mailbox_get_metadata(box, MAILBOX_METADATA_GUID, &metadata) < 0) {
		i_error("Can't get mailbox GUID %s: %s",
			mailbox_get_vname(box),
			mailbox_get_last_error(box, error_r));
		return -1;
	}

	ret = memcmp(mailbox_guid, metadata.guid, sizeof(metadata.guid));
	if (ret > 0) {
		if (brain->debug) {
			i_debug("brain %c: Changing mailbox %s GUID %s -> %s",
				brain->master_brain ? 'M' : 'S',
				mailbox_get_vname(box),
				guid_128_to_string(metadata.guid),
				guid_128_to_string(mailbox_guid));
		}
		memset(&update, 0, sizeof(update));
		memcpy(update.mailbox_guid, mailbox_guid,
		       sizeof(update.mailbox_guid));
		if (mailbox_update(box, &update) < 0) {
			i_error("Can't update mailbox GUID %s: %s",
				mailbox_get_vname(box),
				mailbox_get_last_error(box, error_r));
			return -1;
		}
		/* verify that the update worked */
		if (mailbox_get_metadata(box, MAILBOX_METADATA_GUID,
					 &metadata) < 0) {
			i_error("Can't get mailbox GUID %s: %s",
				mailbox_get_vname(box),
				mailbox_get_last_error(box, error_r));
			return -1;
		}
		if (memcmp(mailbox_guid, metadata.guid,
			   sizeof(metadata.guid)) != 0) {
			i_error("Backend didn't update mailbox %s GUID",
				mailbox_get_vname(box));
			*error_r = MAIL_ERROR_TEMP;
			return -1;
		}
	} else if (ret < 0) {
		if (brain->debug) {
			i_debug("brain %c: Other brain should change mailbox "
				"%s GUID %s -> %s",
				brain->master_brain ? 'M' : 'S',
				mailbox_get_vname(box),
				guid_128_to_string(mailbox_guid),
				guid_128_to_string(metadata.guid));
		}
	}
	return 0;
}
int dsync_brain_mailbox_tree_sync_change(struct dsync_brain *brain,
			const struct dsync_mailbox_tree_sync_change *change,
			enum mail_error *error_r)
{
	struct mailbox *box = NULL, *destbox;
	const char *errstr, *func_name = NULL, *storage_name;
	enum mail_error error;
	int ret = -1;

	if (brain->backup_send) {
		i_assert(brain->no_backup_overwrite);
		return 0;
	}

	switch (change->type) {
	case DSYNC_MAILBOX_TREE_SYNC_TYPE_DELETE_BOX:
		/* make sure we're deleting the correct mailbox */
		ret = dsync_brain_mailbox_alloc(brain, change->mailbox_guid,
						&box, &errstr, error_r);
		if (ret < 0) {
			i_error("Mailbox sync: Couldn't allocate mailbox GUID %s: %s",
				guid_128_to_string(change->mailbox_guid), errstr);
			return -1;
		}
		if (ret == 0) {
			if (brain->debug) {
				i_debug("brain %c: Change during sync: "
					"Mailbox GUID %s deletion conflict: %s",
					brain->master_brain ? 'M' : 'S',
					guid_128_to_string(change->mailbox_guid), errstr);
			}
			brain->changes_during_sync = TRUE;
			return 0;
		}
		break;
	case DSYNC_MAILBOX_TREE_SYNC_TYPE_DELETE_DIR:
		storage_name = mailbox_list_get_storage_name(change->ns->list,
							     change->full_name);
		if (mailbox_list_delete_dir(change->ns->list, storage_name) == 0)
			return 0;

		errstr = mailbox_list_get_last_error(change->ns->list, &error);
		if (error == MAIL_ERROR_NOTFOUND ||
		    error == MAIL_ERROR_EXISTS) {
			if (brain->debug) {
				i_debug("brain %c: Change during sync: "
					"Mailbox %s mailbox_list_delete_dir conflict: %s",
					brain->master_brain ? 'M' : 'S',
					change->full_name, errstr);
			}
			brain->changes_during_sync = TRUE;
			return 0;
		} else {
			i_error("Mailbox sync: mailbox_list_delete_dir failed: %s",
				errstr);
			*error_r = error;
			return -1;
		}
	default:
		box = mailbox_alloc(change->ns->list, change->full_name, 0);
		break;
	}
	switch (change->type) {
	case DSYNC_MAILBOX_TREE_SYNC_TYPE_CREATE_BOX:
		ret = sync_create_box(brain, box, change->mailbox_guid,
				      change->uid_validity, error_r);
		mailbox_free(&box);
		return ret;
	case DSYNC_MAILBOX_TREE_SYNC_TYPE_CREATE_DIR:
		ret = mailbox_create(box, NULL, TRUE);
		if (ret < 0 &&
		    mailbox_get_last_mail_error(box) == MAIL_ERROR_EXISTS) {
			/* it doesn't matter if somebody else created this
			   directory or we automatically did while creating its
			   child mailbox. it's there now anyway and we don't
			   gain anything by treating this failure any
			   differently from success. */
			ret = 0;
		}
		func_name = "mailbox_create";
		break;
	case DSYNC_MAILBOX_TREE_SYNC_TYPE_DELETE_BOX:
		ret = mailbox_delete(box);
		func_name = "mailbox_delete";
		break;
	case DSYNC_MAILBOX_TREE_SYNC_TYPE_DELETE_DIR:
		i_unreached();
	case DSYNC_MAILBOX_TREE_SYNC_TYPE_RENAME:
		destbox = mailbox_alloc(change->ns->list,
					change->rename_dest_name, 0);
		ret = mailbox_rename(box, destbox);
		func_name = "mailbox_rename";
		mailbox_free(&destbox);
		break;
	case DSYNC_MAILBOX_TREE_SYNC_TYPE_SUBSCRIBE:
		ret = mailbox_set_subscribed(box, TRUE);
		func_name = "mailbox_set_subscribed";
		break;
	case DSYNC_MAILBOX_TREE_SYNC_TYPE_UNSUBSCRIBE:
		ret = mailbox_set_subscribed(box, FALSE);
		func_name = "mailbox_set_subscribed";
		break;
	}
	if (ret < 0) {
		errstr = mailbox_get_last_error(box, &error);
		if (error == MAIL_ERROR_EXISTS ||
		    error == MAIL_ERROR_NOTFOUND) {
			/* mailbox was already created or was already deleted.
			   let the next sync figure out what to do */
			if (brain->debug) {
				i_debug("brain %c: Change during sync: "
					"Mailbox %s %s conflict: %s",
					brain->master_brain ? 'M' : 'S',
					mailbox_get_vname(box),
					func_name, errstr);
			}
			brain->changes_during_sync = TRUE;
			ret = 0;
		} else {
			i_error("Mailbox %s sync: %s failed: %s",
				mailbox_get_vname(box), func_name, errstr);
			*error_r = error;
		}
	}
	mailbox_free(&box);
	return ret;
}
Example #20
0
static int
auth_token_read_secret(const char *path,
		       unsigned char secret_r[AUTH_TOKEN_SECRET_LEN])
{
	struct stat st, lst;
	int fd, ret;		

	fd = open(path, O_RDONLY);
	if (fd == -1) {
		if (errno != ENOENT)
			i_error("open(%s) failed: %m", path);
		return -1;
	}

	if (fstat(fd, &st) < 0) {
		i_error("fstat(%s) failed: %m", path);
		i_close_fd(&fd);
		return -1;
	}

	/* check secret len and file type */
	if (st.st_size != AUTH_TOKEN_SECRET_LEN || !S_ISREG(st.st_mode)) {
		i_error("Corrupted token secret file: %s", path);
		i_close_fd(&fd);
		if (unlink(path) < 0)
			i_error("unlink(%s) failed: %m", path);
		return -1;
	}

	/* verify that we're not dealing with a symbolic link */
	if (lstat(path, &lst) < 0) {
		i_error("lstat(%s) failed: %m", path);
		i_close_fd(&fd);
		return -1;		
	}

	/* check security parameters for compromise */
	if ((st.st_mode & 07777) != 0600 ||
	    st.st_uid != geteuid() || st.st_nlink > 1 ||
	    !S_ISREG(lst.st_mode) || st.st_ino != lst.st_ino ||
	    !CMP_DEV_T(st.st_dev, lst.st_dev)) {
		i_error("Compromised token secret file: %s", path);
		i_close_fd(&fd);
		if (unlink(path) < 0)
			i_error("unlink(%s) failed: %m", path);
		return -1;
	}

	/* FIXME: fail here to generate new secret if stored one is too old */

	ret = read_full(fd, secret_r, AUTH_TOKEN_SECRET_LEN);
	if (ret < 0)
		i_error("read(%s) failed: %m", path);
	else if (ret == 0) {
		i_error("Token secret file unexpectedly shrank: %s", path);
		ret = -1;
	}
	if (close(fd) < 0)
		i_error("close(%s) failed: %m", path);

	if (global_auth_settings->debug)
		i_debug("Read auth token secret from %s", path);
	return ret;
}
Example #21
0
static int
ldap_connection_connect_parse(struct ldap_connection *conn,
			      struct ldap_op_queue_entry *req,
			      LDAPMessage *message, bool *finished_r)
{
	int ret, result_err;
	char *retoid, *result_errmsg;
	int msgtype = ldap_msgtype(message);

	*finished_r = TRUE;
	ret = ldap_parse_result(conn->conn, message, &result_err, NULL,
		&result_errmsg, NULL, NULL, 0);

	switch(conn->state) {
	case LDAP_STATE_TLS:
		if (msgtype != LDAP_RES_EXTENDED) {
			*finished_r = FALSE;
			return LDAP_SUCCESS;
		}
		if (ret != 0) {
			ldap_connection_result_failure(conn, req, ret, t_strdup_printf(
				"ldap_start_tls(uri=%s) failed: %s",
				conn->set.uri, ldap_err2string(ret)));
			return ret;
		} else if (result_err != 0) {
			if (conn->set.require_ssl) {
				ldap_connection_result_failure(conn, req, result_err, t_strdup_printf(
					"ldap_start_tls(uri=%s) failed: %s",
					conn->set.uri, result_errmsg));
				ldap_memfree(result_errmsg);
				return LDAP_INVALID_CREDENTIALS; /* make sure it disconnects */
			}
		} else {
			ret = ldap_parse_extended_result(conn->conn, message, &retoid, NULL, 0);
			/* retoid can be NULL even if ret == 0 */
			if (ret == 0) {
				ret = ldap_install_tls(conn->conn);
				if (ret != 0) {
					// if this fails we have to abort
					ldap_connection_result_failure(conn, req, ret, t_strdup_printf(
						"ldap_start_tls(uri=%s) failed: %s",
						conn->set.uri, ldap_err2string(ret)));
					return LDAP_INVALID_CREDENTIALS;
				}
			}
			if (ret != LDAP_SUCCESS) {
				if (conn->set.require_ssl) {
					ldap_connection_result_failure(conn, req, ret, t_strdup_printf(
						"ldap_start_tls(uri=%s) failed: %s",
						conn->set.uri, ldap_err2string(ret)));
					return LDAP_UNAVAILABLE;
				}
			} else {
				if (conn->set.debug > 0)
					i_debug("Using TLS connection to remote LDAP server");
			}
			ldap_memfree(retoid);
		}
		conn->state = LDAP_STATE_AUTH;
		return ldap_connect_next_message(conn, req, finished_r);
	case LDAP_STATE_AUTH:
		if (ret != LDAP_SUCCESS) {
			ldap_connection_result_failure(conn, req, ret, t_strdup_printf(
				"ldap_parse_result() failed for connect: %s",
				ldap_err2string(ret)));
			return ret;
		}
		if (result_err != LDAP_SUCCESS) {
			const char *error = result_errmsg != NULL ?
				result_errmsg : ldap_err2string(result_err);
				ldap_connection_result_failure(conn, req, result_err, t_strdup_printf(
				"Connect failed: %s", error));
			ldap_memfree(result_errmsg);
			return result_err;
		}
		if (msgtype != LDAP_RES_BIND) return 0;
		ret = ldap_parse_sasl_bind_result(conn->conn, message, &(conn->scred), 0);
		if (ret != LDAP_SUCCESS) {
			const char *error = t_strdup_printf(
				"Cannot bind with server: %s", ldap_err2string(ret));
			ldap_connection_result_failure(conn, req, ret, error);
			return 1;
		}
		conn->state = LDAP_STATE_CONNECT;
		return ldap_connect_next_message(conn, req, finished_r);
	default:
		i_unreached();
	}
	return LDAP_SUCCESS;
}
Example #22
0
static int passwd_file_open(struct passwd_file *pw, bool startup,
			    const char **error_r)
{
	const char *no_args = NULL;
	struct istream *input;
	const char *line;
	struct stat st;
	time_t start_time, end_time;
	unsigned int time_secs;
	int fd;

	fd = open(pw->path, O_RDONLY);
	if (fd == -1) {
		if (errno == EACCES)
			*error_r = eacces_error_get("open", pw->path);
		else {
			*error_r = t_strdup_printf("open(%s) failed: %m",
						   pw->path);
		}
		return -1;
	}

	if (fstat(fd, &st) != 0) {
		*error_r = t_strdup_printf("fstat(%s) failed: %m",
					   pw->path);
		i_close_fd(&fd);
		return -1;
	}

	pw->fd = fd;
	pw->stamp = st.st_mtime;
	pw->size = st.st_size;

	pw->pool = pool_alloconly_create(MEMPOOL_GROWING"passwd_file", 10240);
	hash_table_create(&pw->users, pw->pool, 0, str_hash, strcmp);

	start_time = time(NULL);
	input = i_stream_create_fd(pw->fd, (size_t)-1, FALSE);
	i_stream_set_return_partial_line(input, TRUE);
	while ((line = i_stream_read_next_line(input)) != NULL) {
		if (*line == '\0' || *line == ':' || *line == '#')
			continue; /* no username or comment */

		T_BEGIN {
			const char *const *args = t_strsplit(line, ":");
			if (args[1] != NULL) {
				/* at least username+password */
				passwd_file_add(pw, args[0], args[1], args+2);
			} else {
				/* only username */
				passwd_file_add(pw, args[0], NULL, &no_args);
			}
		} T_END;
	}
	i_stream_destroy(&input);
	end_time = time(NULL);
	time_secs = end_time - start_time;

	if ((time_secs > PARSE_TIME_STARTUP_WARN_SECS && startup) ||
	    (time_secs > PARSE_TIME_RELOAD_WARN_SECS && !startup)) {
		i_warning("passwd-file %s: Reading %u users took %u secs",
			  pw->path, hash_table_count(pw->users), time_secs);
	} else if (pw->db->debug) {
		i_debug("passwd-file %s: Read %u users in %u secs",
			pw->path, hash_table_count(pw->users), time_secs);
	}
	return 0;
}
static int set_line(struct mail_storage_service_ctx *ctx,
		    struct mail_storage_service_user *user,
		    const char *line)
{
	struct setting_parser_context *set_parser = user->set_parser;
	bool mail_debug;
	const char *key, *orig_key, *append_value = NULL;
	unsigned int len;
	int ret;

	mail_debug = mail_user_set_get_mail_debug(user->user_info,
						  user->user_set);
	if (strchr(line, '=') == NULL)
		line = t_strconcat(line, "=yes", NULL);
	orig_key = key = t_strcut(line, '=');

	len = strlen(key);
	if (len > 0 && key[len-1] == '+') {
		/* key+=value */
		append_value = line + len + 1;
		key = t_strndup(key, len-1);
	}

	if (!settings_parse_is_valid_key(set_parser, key)) {
		/* assume it's a plugin setting */
		key = t_strconcat("plugin/", key, NULL);
		line = t_strconcat("plugin/", line, NULL);
	}

	if (master_service_set_has_config_override(ctx->service, key)) {
		/* this setting was already overridden with -o parameter */
		if (mail_debug) {
			i_debug("Ignoring overridden (-o) userdb setting: %s",
				key);
		}
		return 1;
	}

	if (append_value != NULL) {
		const void *value;
		enum setting_type type;

		value = settings_parse_get_value(set_parser, key, &type);
		if (type == SET_STR) {
			const char *const *strp = value;

			line = t_strdup_printf("%s=%s%s",
					       key, *strp, append_value);
		} else {
			i_error("Ignoring %s userdb setting. "
				"'+' can only be used for strings.", orig_key);
		}
	}

	ret = settings_parse_line(set_parser, line);
	if (mail_debug && ret >= 0) {
		if (strstr(key, "pass") != NULL) {
			/* possibly a password field (e.g. imapc_password).
			   hide the value. */
			line = t_strconcat(key, "=<hidden>", NULL);
		}
		i_debug(ret == 0 ?
			"Unknown userdb setting: %s" :
			"Added userdb setting: %s", line);
	}
	return ret;
}
Example #24
0
static int client_fetch_url(struct client *client, const char *url,
			    enum imap_urlauth_fetch_flags url_flags)
{
	string_t *response;
	const char *bpstruct, *errormsg;
	bool binary_with_nuls;
	int ret;

	i_assert(client->url == NULL);

	client->msg_part_size = 0;
	client->msg_part_input = NULL;

	if (client->debug)
		i_debug("Fetching URLAUTH %s", url);

	/* fetch URL */
	ret = client_fetch_urlpart(client, url, url_flags, &bpstruct,
				   &binary_with_nuls, &errormsg);
	if (ret <= 0) {
		/* fetch failed */
		if (client->url != NULL)
			imap_msgpart_url_free(&client->url);
		/* don't send error details to anonymous users: just to be sure
		   that no information about the target user account is unduly
		   leaked. */
		if (client->access_anonymous || errormsg == NULL)
			client_send_line(client, "NO");
		else {
			client_send_line(client, "NO\terror=%s",
					 str_tabescape(errormsg));
		}
		if (ret < 0) {
			/* fetch failed badly */
			client_abort(client, "Session aborted: Fatal failure while fetching URL");
		}
		return 0;
	}

	response = t_str_new(256);
	str_append(response, "OK");
	if (binary_with_nuls)
		str_append(response, "\thasnuls");
	if (bpstruct != NULL) {
		str_append(response, "\tbpstruct=");
		str_append(response, str_tabescape(bpstruct));
		if (client->debug) {
			i_debug("Fetched URLAUTH yielded BODYPARTSTRUCTURE (%s)",
				bpstruct);
		}
	}

	/* return content */
	o_stream_cork(client->output);
	if (client->msg_part_size == 0 || client->msg_part_input == NULL) {
		/* empty */
		str_append(response, "\t0");
		client_send_line(client, "%s", str_c(response));

		imap_msgpart_url_free(&client->url);
		client->url = NULL;
		if (client->debug)
			i_debug("Fetched URLAUTH yielded empty result");
	} else {

		/* actual content */
		str_printfa(response, "\t%"PRIuUOFF_T, client->msg_part_size);
		client_send_line(client, "%s", str_c(response));

		if (client->debug) {
			i_debug("Fetched URLAUTH yielded %"PRIuUOFF_T" bytes "
				"of %smessage data", client->msg_part_size,
				(binary_with_nuls ? "binary " : ""));
		}
		if (client_run_url(client) < 0) {
			client_abort(client,
				"Session aborted: Fatal failure while transfering URL");
			return 0;
		}		
	}

	if (client->url != NULL) {
		/* URL not finished */
		o_stream_set_flush_pending(client->output, TRUE);
		client->waiting_input = TRUE;
	}
	o_stream_uncork(client->output);
	return client->url != NULL ? 0 : 1;
}
static int
mail_storage_service_init_post(struct mail_storage_service_ctx *ctx,
			       struct mail_storage_service_user *user,
			       struct mail_storage_service_privileges *priv,
			       struct mail_user **mail_user_r,
			       const char **error_r)
{
	const struct mail_storage_settings *mail_set;
	const char *home = priv->home;
	struct mail_user *mail_user;

	/* NOTE: if more user initialization is added, add it also to
	   mail_user_dup() */
	mail_user = mail_user_alloc(user->input.username, user->user_info,
				    user->user_set);
	mail_user->_service_user = user;
	mail_user_set_home(mail_user, *home == '\0' ? NULL : home);
	mail_user_set_vars(mail_user, ctx->service->name,
			   &user->input.local_ip, &user->input.remote_ip);
	mail_user->uid = priv->uid == (uid_t)-1 ? geteuid() : priv->uid;
	mail_user->gid = priv->gid == (gid_t)-1 ? getegid() : priv->gid;
	mail_user->anonymous = user->anonymous;
	mail_user->admin = user->admin;
	mail_user->auth_token = p_strdup(mail_user->pool, user->auth_token);
	mail_user->auth_user = p_strdup(mail_user->pool, user->auth_user);
	mail_user->session_id =
		p_strdup(mail_user->pool, user->input.session_id);
	mail_user->userdb_fields = user->input.userdb_fields == NULL ? NULL :
		p_strarray_dup(mail_user->pool, user->input.userdb_fields);
	mail_user->autoexpunge_enabled =
		(user->flags & MAIL_STORAGE_SERVICE_FLAG_AUTOEXPUNGE) != 0;
	
	mail_set = mail_user_set_get_storage_set(mail_user);

	if (mail_set->mail_debug) {
		string_t *str = t_str_new(64);

		str_printfa(str, "Effective uid=%s, gid=%s, home=%s",
			    dec2str(geteuid()), dec2str(getegid()), home);
		if (*priv->chroot != '\0')
			str_printfa(str, ", chroot=%s", priv->chroot);
		i_debug("%s", str_c(str));
	}

	if ((user->flags & MAIL_STORAGE_SERVICE_FLAG_TEMP_PRIV_DROP) != 0 &&
	    (user->flags & MAIL_STORAGE_SERVICE_FLAG_ENABLE_CORE_DUMPS) == 0) {
		/* we don't want to write core files to any users' home
		   directories since they could contain information about other
		   users' mails as well. so do no chdiring to home. */
	} else if (*home != '\0' &&
		   (user->flags & MAIL_STORAGE_SERVICE_FLAG_NO_CHDIR) == 0) {
		/* If possible chdir to home directory, so that core file
		   could be written in case we crash. */
		if (chdir(home) < 0) {
			if (errno == EACCES) {
				i_error("%s", eacces_error_get("chdir",
						t_strconcat(home, "/", NULL)));
			} if (errno != ENOENT)
				i_error("chdir(%s) failed: %m", home);
			else if (mail_set->mail_debug)
				i_debug("Home dir not found: %s", home);
		}
	}

	if (mail_user_init(mail_user, error_r) < 0) {
		mail_user_unref(&mail_user);
		return -1;
	}
	if ((user->flags & MAIL_STORAGE_SERVICE_FLAG_NO_NAMESPACES) == 0) {
		if (mail_namespaces_init(mail_user, error_r) < 0) {
			mail_user_unref(&mail_user);
			return -1;
		}
	}
	*mail_user_r = mail_user;
	return 0;
}
Example #26
0
static int
client_handle_user_command(struct client *client, const char *cmd,
			   const char *const *args, const char **error_r)
{
	struct mail_storage_service_input input;
	struct imap_urlauth_worker_settings *set;
	struct mail_storage_service_user *user;
	struct imap_urlauth_config config;
	struct mail_user *mail_user;
	const char *error;
	unsigned int count;
	int ret;

	/* "USER\t"<username> */
	*error_r = NULL;

	/* check command syntax */
	if (strcmp(cmd, "USER") != 0) {
		*error_r = t_strconcat("Unknown or inappropriate command: ",
				       cmd, NULL);
		return -1;
	}

	if (args[0] == NULL || args[1] != NULL) {
		*error_r = "USER: Invalid number of parameters";
		return -1;
	}

	/* lookup user */
	memset(&input, 0, sizeof(input));
	input.module = "imap-urlauth-worker";
	input.service = "imap-urlauth-worker";
	input.username = args[0];

	if (client->debug)
		i_debug("Looking up user %s", input.username);

	ret = mail_storage_service_lookup_next(storage_service, &input,
					       &user, &mail_user, &error);
	if (ret < 0) {
		i_error("Failed to lookup user %s: %s", input.username, error);
		client_abort(client, "Session aborted: Failed to lookup user");
		return 0;
	} else if (ret == 0) {
		if (client->debug)
			i_debug("User %s doesn't exist", input.username);

		client_send_line(client, "NO");
		timeout_remove(&client->to_delay);
		return 1;
	}

	client->debug = mail_user->mail_debug =
		client->debug || mail_user->mail_debug;

	/* drop privileges */
	restrict_access_allow_coredumps(TRUE);

	set = mail_storage_service_user_get_set(user)[1];
	settings_var_expand(&imap_urlauth_worker_setting_parser_info, set,
			    mail_user->pool,
			    mail_user_var_expand_table(mail_user));

	if (set->verbose_proctitle) {
		verbose_proctitle = TRUE;
		imap_urlauth_worker_refresh_proctitle();
	}

	client->service_user = user;
	client->mail_user = mail_user;
	client->set = set;

	if (client->debug) {
		i_debug("Found user account `%s' on behalf of user `%s'",
			mail_user->username, client->access_user);
	}

	/* initialize urlauth context */
	if (*set->imap_urlauth_host == '\0') {
		i_error("imap_urlauth_host setting is not configured for user %s",
			mail_user->username);
		client_send_line(client, "NO");
		client_abort(client, "Session aborted: URLAUTH not configured");
		return 0;
	}

	memset(&config, 0, sizeof(config));
	config.url_host = set->imap_urlauth_host;
	config.url_port = set->imap_urlauth_port;
	config.access_user = client->access_user;
	config.access_anonymous = client->access_anonymous;
	config.access_applications =
		(const void *)array_get(&client->access_apps, &count);
		
	client->urlauth_ctx = imap_urlauth_init(client->mail_user, &config);
	if (client->debug) {
		i_debug("Providing access to user account `%s' on behalf of `%s'",
			mail_user->username, client->access_user);
	}

	i_set_failure_prefix("imap-urlauth[%s](%s->%s): ",
			     my_pid, client->access_user, mail_user->username);

	client_send_line(client, "OK");
	return 1;
}
Example #27
0
static int
client_fetch_urlpart(struct client *client, const char *url,
		     enum imap_urlauth_fetch_flags url_flags,
		     const char **bpstruct_r, bool *binary_with_nuls_r,
		     const char **errormsg_r)
{
	const char *error;
	struct imap_msgpart_open_result mpresult;
	enum mail_error error_code;
	int ret;

	*bpstruct_r = NULL;
	*errormsg_r = NULL;
	*binary_with_nuls_r = FALSE;

	ret = imap_urlauth_fetch(client->urlauth_ctx, url,
				 &client->url, &error_code, &error);
	if (ret <= 0) {
		if (ret < 0)
			return -1;
		error = t_strdup_printf("Failed to fetch URLAUTH \"%s\": %s",
					url, error);
		if (client->debug)
			i_debug("%s", error);
		/* don't leak info about existence/accessibility
		   of mailboxes */
		if (error_code == MAIL_ERROR_PARAMS)
			*errormsg_r = error;
		return 0;
	}

	if ((url_flags & IMAP_URLAUTH_FETCH_FLAG_BINARY) != 0)
		imap_msgpart_url_set_decode_to_binary(client->url);
	if ((url_flags & IMAP_URLAUTH_FETCH_FLAG_BODYPARTSTRUCTURE) != 0) {
		if (imap_msgpart_url_get_bodypartstructure(client->url,
							   bpstruct_r,
							   &error) <= 0) {
			if (ret < 0)
				return -1;
			*errormsg_r = t_strdup_printf(
				"Failed to read URLAUTH \"%s\": %s", url, error);
			if (client->debug)
				i_debug("%s", *errormsg_r);
			return 0;
		}
	}

	/* if requested, read the message part the URL points to */
	if ((url_flags & IMAP_URLAUTH_FETCH_FLAG_BODY) != 0 ||
	    (url_flags & IMAP_URLAUTH_FETCH_FLAG_BINARY) != 0) {
		if (imap_msgpart_url_read_part(client->url, &mpresult, &error) <= 0) {
			if (ret < 0)
				return -1;
			*errormsg_r = t_strdup_printf(
				"Failed to read URLAUTH \"%s\": %s", url, error);
			if (client->debug)
				i_debug("%s", *errormsg_r);
			return 0;
		}
		client->msg_part_size = mpresult.size;
		client->msg_part_input = mpresult.input;
		*binary_with_nuls_r = mpresult.binary_decoded_input_has_nuls;
	}
	return 1;
}
int mail_send_rejection(struct mail_deliver_context *ctx, const char *recipient,
			const char *reason)
{
    struct mail *mail = ctx->src_mail;
    struct istream *input;
    struct smtp_client *smtp_client;
    struct ostream *output;
    const char *return_addr, *hdr;
    const char *value, *msgid, *orig_msgid, *boundary;
    string_t *str;
    int ret;

    if (mail_get_first_header(mail, "Message-ID", &orig_msgid) < 0)
	    orig_msgid = NULL;

    if (mail_get_first_header(mail, "Auto-Submitted", &value) > 0 &&
	strcasecmp(value, "no") != 0) {
	    i_info("msgid=%s: Auto-submitted message discarded: %s",
		   orig_msgid == NULL ? "" : str_sanitize(orig_msgid, 80),
		   str_sanitize(reason, 512));
	    return 0;
    }

    return_addr = mail_deliver_get_return_address(ctx);
    if (return_addr == NULL) {
	    i_info("msgid=%s: Return-Path missing, rejection reason: %s",
		   orig_msgid == NULL ? "" : str_sanitize(orig_msgid, 80),
		   str_sanitize(reason, 512));
	    return 0;
    }

    if (mailbox_get_settings(mail->box)->mail_debug) {
	    i_debug("Sending a rejection to %s: %s", recipient,
		    str_sanitize(reason, 512));
    }

    smtp_client = smtp_client_open(ctx->set, return_addr, NULL, &output);

    msgid = mail_deliver_get_new_message_id(ctx);
    boundary = t_strdup_printf("%s/%s", my_pid, ctx->set->hostname);

    str = t_str_new(512);
    str_printfa(str, "Message-ID: %s\r\n", msgid);
    str_printfa(str, "Date: %s\r\n", message_date_create(ioloop_time));
    str_printfa(str, "From: Mail Delivery Subsystem <%s>\r\n",
		ctx->set->postmaster_address);
    str_printfa(str, "To: <%s>\r\n", return_addr);
    str_append(str, "MIME-Version: 1.0\r\n");
    str_printfa(str, "Content-Type: "
		"multipart/report; report-type=%s;\r\n"
		"\tboundary=\"%s\"\r\n",
		ctx->dsn ? "delivery-status" : "disposition-notification",
		boundary);
    str_append(str, "Subject: ");
    var_expand(str, ctx->set->rejection_subject,
	       get_var_expand_table(mail, reason, recipient));
    str_append(str, "\r\n");

    str_append(str, "Auto-Submitted: auto-replied (rejected)\r\n");
    str_append(str, "Precedence: bulk\r\n");
    str_append(str, "\r\nThis is a MIME-encapsulated message\r\n\r\n");

    /* human readable status report */
    str_printfa(str, "--%s\r\n", boundary);
    str_append(str, "Content-Type: text/plain; charset=utf-8\r\n");
    str_append(str, "Content-Disposition: inline\r\n");
    str_append(str, "Content-Transfer-Encoding: 8bit\r\n\r\n");

    var_expand(str, ctx->set->rejection_reason,
	       get_var_expand_table(mail, reason, recipient));
    str_append(str, "\r\n");

    if (ctx->dsn) {
	    /* DSN status report: For LDA rejects. currently only used when
	       user is out of quota */
	    str_printfa(str, "--%s\r\n"
			"Content-Type: message/delivery-status\r\n\r\n",
			boundary);
	    str_printfa(str, "Reporting-MTA: dns; %s\r\n", ctx->set->hostname);
	    if (mail_get_first_header(mail, "Original-Recipient", &hdr) > 0)
		    str_printfa(str, "Original-Recipient: rfc822; %s\r\n", hdr);
	    str_printfa(str, "Final-Recipient: rfc822; %s\r\n", recipient);
	    str_append(str, "Action: failed\r\n");
	    str_printfa(str, "Status: %s\r\n", ctx->mailbox_full ? "5.2.2" : "5.2.0");
    } else {
	    /* MDN status report: For Sieve "reject" */
	    str_printfa(str, "--%s\r\n"
			"Content-Type: message/disposition-notification\r\n\r\n",
			boundary);
	    str_printfa(str, "Reporting-UA: %s; Dovecot Mail Delivery Agent\r\n",
			ctx->set->hostname);
	    if (mail_get_first_header(mail, "Original-Recipient", &hdr) > 0)
		    str_printfa(str, "Original-Recipient: rfc822; %s\r\n", hdr);
	    str_printfa(str, "Final-Recipient: rfc822; %s\r\n", recipient);

	    if (orig_msgid != NULL)
		    str_printfa(str, "Original-Message-ID: %s\r\n", orig_msgid);
	    str_append(str, "Disposition: "
		       "automatic-action/MDN-sent-automatically; deleted\r\n");
    }
    str_append(str, "\r\n");

    /* original message's headers */
    str_printfa(str, "--%s\r\nContent-Type: message/rfc822\r\n\r\n", boundary);
    o_stream_nsend(output, str_data(str), str_len(str));

    if (mail_get_hdr_stream(mail, NULL, &input) == 0) {
	    /* Note: If you add more headers, they need to be sorted.
	       We'll drop Content-Type because we're not including the message
	       body, and having a multipart Content-Type may confuse some
	       MIME parsers when they don't see the message boundaries. */
	    static const char *const exclude_headers[] = {
		    "Content-Type"
	    };

	    input = i_stream_create_header_filter(input,
	    		HEADER_FILTER_EXCLUDE | HEADER_FILTER_NO_CR |
			HEADER_FILTER_HIDE_BODY, exclude_headers,
			N_ELEMENTS(exclude_headers),
			*null_header_filter_callback, (void *)NULL);

	    ret = o_stream_send_istream(output, input);
	    i_stream_unref(&input);

	    i_assert(ret != 0);
    }

    str_truncate(str, 0);
    str_printfa(str, "\r\n\r\n--%s--\r\n", boundary);
    o_stream_nsend(output, str_data(str), str_len(str));
    return smtp_client_close(smtp_client);
}
Example #29
0
static int
do_rquota_user(struct fs_quota_root *root,
	       uint64_t *bytes_value_r, uint64_t *bytes_limit_r,
	       uint64_t *count_value_r, uint64_t *count_limit_r)
{
	struct getquota_rslt result;
	struct getquota_args args;
	struct timeval timeout;
	enum clnt_stat call_status;
	CLIENT *cl;
	struct fs_quota_mountpoint *mount = root->mount;
	const char *host;
	char *path;

	path = strchr(mount->device_path, ':');
	i_assert(path != NULL);

	host = t_strdup_until(mount->device_path, path);
	path++;

	/* For NFSv4, we send the filesystem path without initial /. Server
	   prepends proper NFS pseudoroot automatically and uses this for
	   detection of NFSv4 mounts. */
	if (strcmp(root->mount->type, "nfs4") == 0) {
		while (*path == '/')
			path++;
	}

	if (root->root.quota->set->debug) {
		i_debug("quota-fs: host=%s, path=%s, uid=%s",
			host, path, dec2str(root->uid));
	}

	/* clnt_create() polls for a while to establish a connection */
	cl = clnt_create(host, RQUOTAPROG, RQUOTAVERS, "udp");
	if (cl == NULL) {
		i_error("quota-fs: could not contact RPC service on %s",
			host);
		return -1;
	}

	/* Establish some RPC credentials */
	auth_destroy(cl->cl_auth);
	cl->cl_auth = authunix_create_default();

	/* make the rquota call on the remote host */
	args.gqa_pathp = path;
	args.gqa_uid = root->uid;

	timeout.tv_sec = RQUOTA_GETQUOTA_TIMEOUT_SECS;
	timeout.tv_usec = 0;
	call_status = clnt_call(cl, RQUOTAPROC_GETQUOTA,
				(xdrproc_t)xdr_getquota_args, (char *)&args,
				(xdrproc_t)xdr_getquota_rslt, (char *)&result,
				timeout);
	
	/* the result has been deserialized, let the client go */
	auth_destroy(cl->cl_auth);
	clnt_destroy(cl);

	if (call_status != RPC_SUCCESS) {
		const char *rpc_error_msg = clnt_sperrno(call_status);

		i_error("quota-fs: remote rquota call failed: %s",
			rpc_error_msg);
		return -1;
	}

	switch (result.status) {
	case Q_OK: {
		rquota_get_result(&result.getquota_rslt_u.gqr_rquota,
				  bytes_value_r, bytes_limit_r,
				  count_value_r, count_limit_r);
		if (root->root.quota->set->debug) {
			i_debug("quota-fs: uid=%s, bytes=%llu/%llu files=%llu/%llu",
				dec2str(root->uid),
				(unsigned long long)*bytes_value_r,
				(unsigned long long)*bytes_limit_r,
				(unsigned long long)*count_value_r,
				(unsigned long long)*count_limit_r);
		}
		return 1;
	}
	case Q_NOQUOTA:
		if (root->root.quota->set->debug) {
			i_debug("quota-fs: uid=%s, limit=unlimited",
				dec2str(root->uid));
		}
		fs_quota_root_disable(root, FALSE);
		return 0;
	case Q_EPERM:
		i_error("quota-fs: permission denied to rquota service");
		return -1;
	default:
		i_error("quota-fs: unrecognized status code (%d) "
			"from rquota service", result.status);
		return -1;
	}
}
static int
acl_backend_vfile_read(struct acl_object *aclobj, bool global, const char *path,
		       struct acl_vfile_validity *validity, bool try_retry,
		       bool *is_dir_r)
{
	struct istream *input;
	struct stat st;
	struct acl_rights rights;
	const char *line, *error;
	unsigned int linenum;
	int fd, ret = 0;

	*is_dir_r = FALSE;

	fd = nfs_safe_open(path, O_RDONLY);
	if (fd == -1) {
		if (errno == ENOENT || errno == ENOTDIR) {
			if (aclobj->backend->debug)
				i_debug("acl vfile: file %s not found", path);
			validity->last_mtime = ACL_VFILE_VALIDITY_MTIME_NOTFOUND;
		} else if (errno == EACCES) {
			if (aclobj->backend->debug)
				i_debug("acl vfile: no access to file %s",
					path);

			acl_object_remove_all_access(aclobj);
			validity->last_mtime = ACL_VFILE_VALIDITY_MTIME_NOACCESS;
		} else {
			i_error("open(%s) failed: %m", path);
			return -1;
		}

		validity->last_size = 0;
		validity->last_read_time = ioloop_time;
		return 1;
	}

	if (fstat(fd, &st) < 0) {
		if (errno == ESTALE && try_retry) {
			i_close_fd(&fd);
			return 0;
		}

		i_error("fstat(%s) failed: %m", path);
		i_close_fd(&fd);
		return -1;
	}
	if (S_ISDIR(st.st_mode)) {
		/* we opened a directory. */
		*is_dir_r = TRUE;
		i_close_fd(&fd);
		return 0;
	}

	if (aclobj->backend->debug)
		i_debug("acl vfile: reading file %s", path);

	input = i_stream_create_fd(fd, (size_t)-1, FALSE);
	i_stream_set_return_partial_line(input, TRUE);
	linenum = 1;
	while ((line = i_stream_read_next_line(input)) != NULL) {
		T_BEGIN {
			ret = acl_rights_parse_line(line, aclobj->rights_pool,
						    &rights, &error);
			rights.global = global;
			if (ret < 0) {
				i_error("ACL file %s line %u: %s",
					path, linenum, error);
			} else {
				array_append(&aclobj->rights, &rights, 1);
			}
		} T_END;
		if (ret < 0)
			break;
		linenum++;
	}

	if (ret < 0) {
		/* parsing failure */
	} else if (input->stream_errno != 0) {
		if (input->stream_errno == ESTALE && try_retry)
			ret = 0;
		else {
			ret = -1;
			i_error("read(%s) failed: %m", path);
		}
	} else {
		if (fstat(fd, &st) < 0) {
			if (errno == ESTALE && try_retry)
				ret = 0;
			else {
				ret = -1;
				i_error("fstat(%s) failed: %m", path);
			}
		} else {
			ret = 1;
			validity->last_read_time = ioloop_time;
			validity->last_mtime = st.st_mtime;
			validity->last_size = st.st_size;
		}
	}

	i_stream_unref(&input);
	if (close(fd) < 0) {
		if (errno == ESTALE && try_retry)
			return 0;

		i_error("close(%s) failed: %m", path);
		return -1;
	}
	return ret;
}