Example #1
0
int main(int argc, char* argv[]) {
    keyval_t kv;

    if (argc < 3) {
        hk_err("Usage: keyval key val\n");
        return 1;
    }
    
    char* key = argv[1];
    char* val = argv[2];

    set_keyval(&kv, key, val);
    hk_info("%s: %s\n", kv.key, kv.val);

    destroy_keyval(&kv);

    return 0;
}
static int
user_reply_handle(struct mail_storage_service_ctx *ctx,
		  struct mail_storage_service_user *user,
		  const struct auth_user_reply *reply,
		  const char **error_r)
{
	const char *home = reply->home;
	const char *chroot = reply->chroot;
	const char *const *str, *line, *p;
	unsigned int i, count;
	int ret = 0;

	if (reply->uid != (uid_t)-1) {
		if (reply->uid == 0) {
			*error_r = "userdb returned 0 as uid";
			return -1;
		}
		user->uid_source = "userdb lookup";
		set_keyval(ctx, user, "mail_uid", dec2str(reply->uid));
	}
	if (reply->gid != (uid_t)-1) {
		user->gid_source = "userdb lookup";
		set_keyval(ctx, user, "mail_gid", dec2str(reply->gid));
	}

	if (home != NULL && chroot == NULL &&
	    *user->user_set->valid_chroot_dirs != '\0' &&
	    (p = strstr(home, "/./")) != NULL) {
		/* wu-ftpd like <chroot>/./<home> - check only if there's even
		   a possibility of using them (non-empty valid_chroot_dirs) */
		chroot = t_strdup_until(home, p);
		home = p + 2;
	}

	if (home != NULL)
		set_keyval(ctx, user, "mail_home", home);

	if (chroot != NULL) {
		if (!validate_chroot(user->user_set, chroot)) {
			*error_r = t_strdup_printf(
				"userdb returned invalid chroot directory: %s "
				"(see valid_chroot_dirs setting)", chroot);
			return -1;
		}
		set_keyval(ctx, user, "mail_chroot", chroot);
	}

	user->anonymous = reply->anonymous;

	str = array_get(&reply->extra_fields, &count);
	for (i = 0; i < count; i++) {
		line = str[i];
		if (strncmp(line, "system_groups_user="******"nice=", 5) == 0) {
#ifdef HAVE_SETPRIORITY
			int n;
			if (str_to_int(line + 5, &n) < 0) {
				i_error("userdb returned invalid nice value %s",
					line + 5);
			} else if (n != 0) {
				if (setpriority(PRIO_PROCESS, 0, n) < 0)
					i_error("setpriority(%d) failed: %m", n);
			}
#endif
		} else if (strncmp(line, "auth_token=", 11) == 0) {
			user->auth_token = p_strdup(user->pool, line+11);
		} else if (strncmp(line, "auth_user="******"admin=", 6) == 0) {
			user->admin = line[6] == 'y' || line[6] == 'Y' ||
				line[6] == '1';
		} else T_BEGIN {
			ret = set_line(ctx, user, line);
		} T_END;
		if (ret < 0)
			break;
	}

	if (ret < 0) {
		*error_r = t_strdup_printf("Invalid userdb input '%s': %s",
			str[i], settings_parser_get_error(user->set_parser));
	}
	return ret;
}
static int
mail_storage_service_next_real(struct mail_storage_service_ctx *ctx,
			       struct mail_storage_service_user *user,
			       struct mail_user **mail_user_r)
{
	struct mail_storage_service_privileges priv;
	const char *error;
	unsigned int len;
	bool disallow_root =
		(user->flags & MAIL_STORAGE_SERVICE_FLAG_DISALLOW_ROOT) != 0;
	bool temp_priv_drop =
		(user->flags & MAIL_STORAGE_SERVICE_FLAG_TEMP_PRIV_DROP) != 0;
	bool use_chroot;

	if (service_parse_privileges(ctx, user, &priv, &error) < 0) {
		i_error("%s", error);
		return -2;
	}

	if (*priv.home != '/' && *priv.home != '\0') {
		i_error("Relative home directory paths not supported: %s",
			priv.home);
		return -2;
	}

	/* we can't chroot if we want to switch between users. there's
	   not much point either (from security point of view). but if we're
	   already chrooted, we'll just have to continue and hope that the
	   current chroot is the same as the wanted chroot */
	use_chroot = !temp_priv_drop ||
		restrict_access_get_current_chroot() != NULL;

	len = strlen(priv.chroot);
	if (len > 2 && strcmp(priv.chroot + len - 2, "/.") == 0 &&
	    strncmp(priv.home, priv.chroot, len - 2) == 0) {
		/* mail_chroot = /chroot/. means that the home dir already
		   contains the chroot dir. remove it from home. */
		if (use_chroot) {
			priv.home += len - 2;
			if (*priv.home == '\0')
				priv.home = "/";
			priv.chroot = t_strndup(priv.chroot, len - 2);

			set_keyval(ctx, user, "mail_home", priv.home);
			set_keyval(ctx, user, "mail_chroot", priv.chroot);
		}
	} else if (len > 0 && !use_chroot) {
		/* we're not going to chroot. fix home directory so we can
		   access it. */
		if (*priv.home == '\0' || strcmp(priv.home, "/") == 0)
			priv.home = priv.chroot;
		else
			priv.home = t_strconcat(priv.chroot, priv.home, NULL);
		priv.chroot = "";
		set_keyval(ctx, user, "mail_home", priv.home);
	}

	/* create ioloop context regardless of logging. it's also used by
	   stats plugin. */
	user->ioloop_ctx = io_loop_context_new(current_ioloop);

	if ((user->flags & MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT) == 0)
		mail_storage_service_init_log(ctx, user, &priv);

	if ((user->flags & MAIL_STORAGE_SERVICE_FLAG_NO_RESTRICT_ACCESS) == 0) {
		if (service_drop_privileges(user, &priv,
					    disallow_root, temp_priv_drop,
					    FALSE, &error) < 0) {
			i_error("Couldn't drop privileges: %s", error);
			return -1;
		}
		if (!temp_priv_drop ||
		    (user->flags & MAIL_STORAGE_SERVICE_FLAG_ENABLE_CORE_DUMPS) != 0)
			restrict_access_allow_coredumps(TRUE);
	}

	/* privileges are dropped. initialize plugins that haven't been
	   initialized yet. */
	module_dir_init(mail_storage_service_modules);

	if (mail_storage_service_init_post(ctx, user, &priv,
					   mail_user_r, &error) < 0) {
		i_error("User initialization failed: %s", error);
		return -2;
	}
	return 0;
}
static int
mail_storage_service_lookup_real(struct mail_storage_service_ctx *ctx,
				 const struct mail_storage_service_input *input,
				 bool update_log_prefix,
				 struct mail_storage_service_user **user_r,
				 const char **error_r)
{
	enum mail_storage_service_flags flags;
	struct mail_storage_service_user *user;
	const char *username = input->username;
	const struct setting_parser_info *user_info;
	const struct mail_user_settings *user_set;
	const char *const *userdb_fields, *error;
	struct auth_user_reply reply;
	const struct setting_parser_context *set_parser;
	void **sets;
	pool_t user_pool, temp_pool;
	int ret = 1;

	user_pool = pool_alloconly_create(MEMPOOL_GROWING"mail storage service user", 1024*6);
	flags = mail_storage_service_input_get_flags(ctx, input);

	if ((flags & MAIL_STORAGE_SERVICE_FLAG_TEMP_PRIV_DROP) != 0 &&
	    geteuid() != 0) {
		/* we dropped privileges only temporarily. switch back to root
		   before reading settings, so we'll definitely have enough
		   permissions to connect to the config socket. */
		mail_storage_service_seteuid_root();
	}

	if (mail_storage_service_read_settings(ctx, input, user_pool,
					       &user_info, &set_parser,
					       &error) < 0) {
		if (ctx->config_permission_denied) {
			/* just restart and maybe next time we will open the
			   config socket before dropping privileges */
			i_fatal("%s", error);
		}
		i_error("%s", error);
		pool_unref(&user_pool);
		*error_r = MAIL_ERRSTR_CRITICAL_MSG;
		return -1;
	}

	if ((flags & MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT) == 0 &&
	    !ctx->log_initialized) {
		/* initialize logging again, in case we only read the
		   settings for the first above */
		ctx->log_initialized = TRUE;
		master_service_init_log(ctx->service,
			t_strconcat(ctx->service->name, ": ", NULL));
		update_log_prefix = TRUE;
	}
	sets = master_service_settings_parser_get_others(master_service,
							 set_parser);
	user_set = sets[0];

	if (update_log_prefix)
		mail_storage_service_set_log_prefix(ctx, user_set, NULL, input, NULL);

	if (ctx->conn == NULL)
		mail_storage_service_first_init(ctx, user_info, user_set);
	/* load global plugins */
	if (mail_storage_service_load_modules(ctx, user_info, user_set, &error) < 0) {
		i_error("%s", error);
		pool_unref(&user_pool);
		*error_r = MAIL_ERRSTR_CRITICAL_MSG;
		return -1;
	}

	if (ctx->userdb_next_pool == NULL)
		temp_pool = pool_alloconly_create("userdb lookup", 2048);
	else {
		temp_pool = ctx->userdb_next_pool;
		ctx->userdb_next_pool = NULL;
		pool_ref(temp_pool);
	}
	if ((flags & MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP) != 0) {
		ret = service_auth_userdb_lookup(ctx, input, temp_pool,
						 &username, &userdb_fields,
						 error_r);
		if (ret <= 0) {
			pool_unref(&temp_pool);
			pool_unref(&user_pool);
			return ret;
		}
		if (ctx->userdb_next_fieldsp != NULL)
			*ctx->userdb_next_fieldsp = userdb_fields;
	} else {
		userdb_fields = input->userdb_fields;
	}

	user = p_new(user_pool, struct mail_storage_service_user, 1);
	user->service_ctx = ctx;
	user->pool = user_pool;
	user->input = *input;
	user->input.userdb_fields = userdb_fields == NULL ? NULL :
		p_strarray_dup(user_pool, userdb_fields);
	user->input.username = p_strdup(user_pool, username);
	user->input.session_id = p_strdup(user_pool, input->session_id);
	if (user->input.session_id == NULL) {
		user->input.session_id =
			mail_storage_service_generate_session_id(user_pool,
				input->session_id_prefix);
	}
	user->user_info = user_info;
	user->flags = flags;

	user->set_parser = settings_parser_dup(set_parser, user_pool);

	sets = master_service_settings_parser_get_others(master_service,
							 user->set_parser);
	user->user_set = sets[0];
	user->gid_source = "mail_gid setting";
	user->uid_source = "mail_uid setting";

	if ((flags & MAIL_STORAGE_SERVICE_FLAG_DEBUG) != 0)
		(void)settings_parse_line(user->set_parser, "mail_debug=yes");

	if ((flags & MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP) == 0) {
		const char *home = getenv("HOME");
		if (home != NULL)
			set_keyval(ctx, user, "mail_home", home);
	}

	if (userdb_fields != NULL) {
		auth_user_fields_parse(userdb_fields, temp_pool, &reply);
		array_sort(&reply.extra_fields, extra_field_key_cmp_p);
		if (user_reply_handle(ctx, user, &reply, &error) < 0) {
			i_error("Invalid settings in userdb: %s", error);
			*error_r = ERRSTR_INVALID_USER_SETTINGS;
			ret = -2;
		}
	}
	if (ret > 0 && !settings_parser_check(user->set_parser, user_pool, &error)) {
		i_error("Invalid settings (probably caused by userdb): %s", error);
		*error_r = ERRSTR_INVALID_USER_SETTINGS;
		ret = -2;
	}
	pool_unref(&temp_pool);

	/* load per-user plugins */
	if (ret > 0) {
		if (mail_storage_service_load_modules(ctx, user_info,
						      user->user_set,
						      &error) < 0) {
			i_error("%s", error);
			*error_r = MAIL_ERRSTR_CRITICAL_MSG;
			ret = -2;
		}
	}

	*user_r = user;
	return ret;
}