162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/* CacheFiles security management
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) 2007, 2021 Red Hat, Inc. All Rights Reserved.
562306a36Sopenharmony_ci * Written by David Howells (dhowells@redhat.com)
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/fs.h>
962306a36Sopenharmony_ci#include <linux/cred.h>
1062306a36Sopenharmony_ci#include "internal.h"
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci/*
1362306a36Sopenharmony_ci * determine the security context within which we access the cache from within
1462306a36Sopenharmony_ci * the kernel
1562306a36Sopenharmony_ci */
1662306a36Sopenharmony_ciint cachefiles_get_security_ID(struct cachefiles_cache *cache)
1762306a36Sopenharmony_ci{
1862306a36Sopenharmony_ci	struct cred *new;
1962306a36Sopenharmony_ci	int ret;
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci	_enter("{%s}", cache->secctx);
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci	new = prepare_kernel_cred(current);
2462306a36Sopenharmony_ci	if (!new) {
2562306a36Sopenharmony_ci		ret = -ENOMEM;
2662306a36Sopenharmony_ci		goto error;
2762306a36Sopenharmony_ci	}
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	if (cache->secctx) {
3062306a36Sopenharmony_ci		ret = set_security_override_from_ctx(new, cache->secctx);
3162306a36Sopenharmony_ci		if (ret < 0) {
3262306a36Sopenharmony_ci			put_cred(new);
3362306a36Sopenharmony_ci			pr_err("Security denies permission to nominate security context: error %d\n",
3462306a36Sopenharmony_ci			       ret);
3562306a36Sopenharmony_ci			goto error;
3662306a36Sopenharmony_ci		}
3762306a36Sopenharmony_ci	}
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	cache->cache_cred = new;
4062306a36Sopenharmony_ci	ret = 0;
4162306a36Sopenharmony_cierror:
4262306a36Sopenharmony_ci	_leave(" = %d", ret);
4362306a36Sopenharmony_ci	return ret;
4462306a36Sopenharmony_ci}
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci/*
4762306a36Sopenharmony_ci * see if mkdir and create can be performed in the root directory
4862306a36Sopenharmony_ci */
4962306a36Sopenharmony_cistatic int cachefiles_check_cache_dir(struct cachefiles_cache *cache,
5062306a36Sopenharmony_ci				      struct dentry *root)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	int ret;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	ret = security_inode_mkdir(d_backing_inode(root), root, 0);
5562306a36Sopenharmony_ci	if (ret < 0) {
5662306a36Sopenharmony_ci		pr_err("Security denies permission to make dirs: error %d",
5762306a36Sopenharmony_ci		       ret);
5862306a36Sopenharmony_ci		return ret;
5962306a36Sopenharmony_ci	}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	ret = security_inode_create(d_backing_inode(root), root, 0);
6262306a36Sopenharmony_ci	if (ret < 0)
6362306a36Sopenharmony_ci		pr_err("Security denies permission to create files: error %d",
6462306a36Sopenharmony_ci		       ret);
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	return ret;
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci/*
7062306a36Sopenharmony_ci * check the security details of the on-disk cache
7162306a36Sopenharmony_ci * - must be called with security override in force
7262306a36Sopenharmony_ci * - must return with a security override in force - even in the case of an
7362306a36Sopenharmony_ci *   error
7462306a36Sopenharmony_ci */
7562306a36Sopenharmony_ciint cachefiles_determine_cache_security(struct cachefiles_cache *cache,
7662306a36Sopenharmony_ci					struct dentry *root,
7762306a36Sopenharmony_ci					const struct cred **_saved_cred)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	struct cred *new;
8062306a36Sopenharmony_ci	int ret;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	_enter("");
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	/* duplicate the cache creds for COW (the override is currently in
8562306a36Sopenharmony_ci	 * force, so we can use prepare_creds() to do this) */
8662306a36Sopenharmony_ci	new = prepare_creds();
8762306a36Sopenharmony_ci	if (!new)
8862306a36Sopenharmony_ci		return -ENOMEM;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	cachefiles_end_secure(cache, *_saved_cred);
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	/* use the cache root dir's security context as the basis with
9362306a36Sopenharmony_ci	 * which create files */
9462306a36Sopenharmony_ci	ret = set_create_files_as(new, d_backing_inode(root));
9562306a36Sopenharmony_ci	if (ret < 0) {
9662306a36Sopenharmony_ci		abort_creds(new);
9762306a36Sopenharmony_ci		cachefiles_begin_secure(cache, _saved_cred);
9862306a36Sopenharmony_ci		_leave(" = %d [cfa]", ret);
9962306a36Sopenharmony_ci		return ret;
10062306a36Sopenharmony_ci	}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	put_cred(cache->cache_cred);
10362306a36Sopenharmony_ci	cache->cache_cred = new;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	cachefiles_begin_secure(cache, _saved_cred);
10662306a36Sopenharmony_ci	ret = cachefiles_check_cache_dir(cache, root);
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	if (ret == -EOPNOTSUPP)
10962306a36Sopenharmony_ci		ret = 0;
11062306a36Sopenharmony_ci	_leave(" = %d", ret);
11162306a36Sopenharmony_ci	return ret;
11262306a36Sopenharmony_ci}
113