Commit 937fd403 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'afs-fixes-20231221' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs

Pull AFS fixes from David Howells:
 "Improve the interaction of arbitrary lookups in the AFS dynamic root
  that hit DNS lookup failures [1] where kafs behaves differently from
  openafs and causes some applications to fail that aren't expecting
  that. Further, negative DNS results aren't getting removed and are
  causing failures to persist.

   - Always delete unused (particularly negative) dentries as soon as
     possible so that they don't prevent future lookups from retrying.

   - Fix the handling of new-style negative DNS lookups in ->lookup() to
     make them return ENOENT so that userspace doesn't get confused when
     stat succeeds but the following open on the looked up file then
     fails.

   - Fix key handling so that DNS lookup results are reclaimed almost as
     soon as they expire rather than sitting round either forever or for
     an additional 5 mins beyond a set expiry time returning
     EKEYEXPIRED. They persist for 1s as /bin/ls will do a second stat
     call if the first fails"

Link: https://bugzilla.kernel.org/show_bug.cgi?id=216637 [1]
Reviewed-by: default avatarJeffrey Altman <jaltman@auristor.com>

* tag 'afs-fixes-20231221' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs:
  keys, dns: Allow key types (eg. DNS) to be reclaimed immediately on expiry
  afs: Fix dynamic root lookup DNS check
  afs: Fix the dynamic root's d_delete to always delete unused dentries
parents 13b73446 39299bdd
...@@ -114,6 +114,7 @@ static int afs_probe_cell_name(struct dentry *dentry) ...@@ -114,6 +114,7 @@ static int afs_probe_cell_name(struct dentry *dentry)
struct afs_net *net = afs_d2net(dentry); struct afs_net *net = afs_d2net(dentry);
const char *name = dentry->d_name.name; const char *name = dentry->d_name.name;
size_t len = dentry->d_name.len; size_t len = dentry->d_name.len;
char *result = NULL;
int ret; int ret;
/* Names prefixed with a dot are R/W mounts. */ /* Names prefixed with a dot are R/W mounts. */
...@@ -131,9 +132,22 @@ static int afs_probe_cell_name(struct dentry *dentry) ...@@ -131,9 +132,22 @@ static int afs_probe_cell_name(struct dentry *dentry)
} }
ret = dns_query(net->net, "afsdb", name, len, "srv=1", ret = dns_query(net->net, "afsdb", name, len, "srv=1",
NULL, NULL, false); &result, NULL, false);
if (ret == -ENODATA || ret == -ENOKEY) if (ret == -ENODATA || ret == -ENOKEY || ret == 0)
ret = -ENOENT; ret = -ENOENT;
if (ret > 0 && ret >= sizeof(struct dns_server_list_v1_header)) {
struct dns_server_list_v1_header *v1 = (void *)result;
if (v1->hdr.zero == 0 &&
v1->hdr.content == DNS_PAYLOAD_IS_SERVER_LIST &&
v1->hdr.version == 1 &&
(v1->status != DNS_LOOKUP_GOOD &&
v1->status != DNS_LOOKUP_GOOD_WITH_BAD))
return -ENOENT;
}
kfree(result);
return ret; return ret;
} }
...@@ -252,20 +266,9 @@ static int afs_dynroot_d_revalidate(struct dentry *dentry, unsigned int flags) ...@@ -252,20 +266,9 @@ static int afs_dynroot_d_revalidate(struct dentry *dentry, unsigned int flags)
return 1; return 1;
} }
/*
* Allow the VFS to enquire as to whether a dentry should be unhashed (mustn't
* sleep)
* - called from dput() when d_count is going to 0.
* - return 1 to request dentry be unhashed, 0 otherwise
*/
static int afs_dynroot_d_delete(const struct dentry *dentry)
{
return d_really_is_positive(dentry);
}
const struct dentry_operations afs_dynroot_dentry_operations = { const struct dentry_operations afs_dynroot_dentry_operations = {
.d_revalidate = afs_dynroot_d_revalidate, .d_revalidate = afs_dynroot_d_revalidate,
.d_delete = afs_dynroot_d_delete, .d_delete = always_delete_dentry,
.d_release = afs_d_release, .d_release = afs_d_release,
.d_automount = afs_d_automount, .d_automount = afs_d_automount,
}; };
......
...@@ -73,6 +73,7 @@ struct key_type { ...@@ -73,6 +73,7 @@ struct key_type {
unsigned int flags; unsigned int flags;
#define KEY_TYPE_NET_DOMAIN 0x00000001 /* Keys of this type have a net namespace domain */ #define KEY_TYPE_NET_DOMAIN 0x00000001 /* Keys of this type have a net namespace domain */
#define KEY_TYPE_INSTANT_REAP 0x00000002 /* Keys of this type don't have a delay after expiring */
/* vet a description */ /* vet a description */
int (*vet_description)(const char *description); int (*vet_description)(const char *description);
......
...@@ -91,6 +91,7 @@ const struct cred *dns_resolver_cache; ...@@ -91,6 +91,7 @@ const struct cred *dns_resolver_cache;
static int static int
dns_resolver_preparse(struct key_preparsed_payload *prep) dns_resolver_preparse(struct key_preparsed_payload *prep)
{ {
const struct dns_server_list_v1_header *v1;
const struct dns_payload_header *bin; const struct dns_payload_header *bin;
struct user_key_payload *upayload; struct user_key_payload *upayload;
unsigned long derrno; unsigned long derrno;
...@@ -122,6 +123,13 @@ dns_resolver_preparse(struct key_preparsed_payload *prep) ...@@ -122,6 +123,13 @@ dns_resolver_preparse(struct key_preparsed_payload *prep)
return -EINVAL; return -EINVAL;
} }
v1 = (const struct dns_server_list_v1_header *)bin;
if ((v1->status != DNS_LOOKUP_GOOD &&
v1->status != DNS_LOOKUP_GOOD_WITH_BAD)) {
if (prep->expiry == TIME64_MAX)
prep->expiry = ktime_get_real_seconds() + 1;
}
result_len = datalen; result_len = datalen;
goto store_result; goto store_result;
} }
...@@ -314,7 +322,7 @@ static long dns_resolver_read(const struct key *key, ...@@ -314,7 +322,7 @@ static long dns_resolver_read(const struct key *key,
struct key_type key_type_dns_resolver = { struct key_type key_type_dns_resolver = {
.name = "dns_resolver", .name = "dns_resolver",
.flags = KEY_TYPE_NET_DOMAIN, .flags = KEY_TYPE_NET_DOMAIN | KEY_TYPE_INSTANT_REAP,
.preparse = dns_resolver_preparse, .preparse = dns_resolver_preparse,
.free_preparse = dns_resolver_free_preparse, .free_preparse = dns_resolver_free_preparse,
.instantiate = generic_key_instantiate, .instantiate = generic_key_instantiate,
......
...@@ -66,6 +66,19 @@ void key_schedule_gc(time64_t gc_at) ...@@ -66,6 +66,19 @@ void key_schedule_gc(time64_t gc_at)
} }
} }
/*
* Set the expiration time on a key.
*/
void key_set_expiry(struct key *key, time64_t expiry)
{
key->expiry = expiry;
if (expiry != TIME64_MAX) {
if (!(key->type->flags & KEY_TYPE_INSTANT_REAP))
expiry += key_gc_delay;
key_schedule_gc(expiry);
}
}
/* /*
* Schedule a dead links collection run. * Schedule a dead links collection run.
*/ */
...@@ -176,7 +189,6 @@ static void key_garbage_collector(struct work_struct *work) ...@@ -176,7 +189,6 @@ static void key_garbage_collector(struct work_struct *work)
static u8 gc_state; /* Internal persistent state */ static u8 gc_state; /* Internal persistent state */
#define KEY_GC_REAP_AGAIN 0x01 /* - Need another cycle */ #define KEY_GC_REAP_AGAIN 0x01 /* - Need another cycle */
#define KEY_GC_REAPING_LINKS 0x02 /* - We need to reap links */ #define KEY_GC_REAPING_LINKS 0x02 /* - We need to reap links */
#define KEY_GC_SET_TIMER 0x04 /* - We need to restart the timer */
#define KEY_GC_REAPING_DEAD_1 0x10 /* - We need to mark dead keys */ #define KEY_GC_REAPING_DEAD_1 0x10 /* - We need to mark dead keys */
#define KEY_GC_REAPING_DEAD_2 0x20 /* - We need to reap dead key links */ #define KEY_GC_REAPING_DEAD_2 0x20 /* - We need to reap dead key links */
#define KEY_GC_REAPING_DEAD_3 0x40 /* - We need to reap dead keys */ #define KEY_GC_REAPING_DEAD_3 0x40 /* - We need to reap dead keys */
...@@ -184,21 +196,17 @@ static void key_garbage_collector(struct work_struct *work) ...@@ -184,21 +196,17 @@ static void key_garbage_collector(struct work_struct *work)
struct rb_node *cursor; struct rb_node *cursor;
struct key *key; struct key *key;
time64_t new_timer, limit; time64_t new_timer, limit, expiry;
kenter("[%lx,%x]", key_gc_flags, gc_state); kenter("[%lx,%x]", key_gc_flags, gc_state);
limit = ktime_get_real_seconds(); limit = ktime_get_real_seconds();
if (limit > key_gc_delay)
limit -= key_gc_delay;
else
limit = key_gc_delay;
/* Work out what we're going to be doing in this pass */ /* Work out what we're going to be doing in this pass */
gc_state &= KEY_GC_REAPING_DEAD_1 | KEY_GC_REAPING_DEAD_2; gc_state &= KEY_GC_REAPING_DEAD_1 | KEY_GC_REAPING_DEAD_2;
gc_state <<= 1; gc_state <<= 1;
if (test_and_clear_bit(KEY_GC_KEY_EXPIRED, &key_gc_flags)) if (test_and_clear_bit(KEY_GC_KEY_EXPIRED, &key_gc_flags))
gc_state |= KEY_GC_REAPING_LINKS | KEY_GC_SET_TIMER; gc_state |= KEY_GC_REAPING_LINKS;
if (test_and_clear_bit(KEY_GC_REAP_KEYTYPE, &key_gc_flags)) if (test_and_clear_bit(KEY_GC_REAP_KEYTYPE, &key_gc_flags))
gc_state |= KEY_GC_REAPING_DEAD_1; gc_state |= KEY_GC_REAPING_DEAD_1;
...@@ -233,8 +241,11 @@ static void key_garbage_collector(struct work_struct *work) ...@@ -233,8 +241,11 @@ static void key_garbage_collector(struct work_struct *work)
} }
} }
if (gc_state & KEY_GC_SET_TIMER) { expiry = key->expiry;
if (key->expiry > limit && key->expiry < new_timer) { if (expiry != TIME64_MAX) {
if (!(key->type->flags & KEY_TYPE_INSTANT_REAP))
expiry += key_gc_delay;
if (expiry > limit && expiry < new_timer) {
kdebug("will expire %x in %lld", kdebug("will expire %x in %lld",
key_serial(key), key->expiry - limit); key_serial(key), key->expiry - limit);
new_timer = key->expiry; new_timer = key->expiry;
...@@ -276,7 +287,7 @@ static void key_garbage_collector(struct work_struct *work) ...@@ -276,7 +287,7 @@ static void key_garbage_collector(struct work_struct *work)
*/ */
kdebug("pass complete"); kdebug("pass complete");
if (gc_state & KEY_GC_SET_TIMER && new_timer != (time64_t)TIME64_MAX) { if (new_timer != TIME64_MAX) {
new_timer += key_gc_delay; new_timer += key_gc_delay;
key_schedule_gc(new_timer); key_schedule_gc(new_timer);
} }
......
...@@ -167,6 +167,7 @@ extern unsigned key_gc_delay; ...@@ -167,6 +167,7 @@ extern unsigned key_gc_delay;
extern void keyring_gc(struct key *keyring, time64_t limit); extern void keyring_gc(struct key *keyring, time64_t limit);
extern void keyring_restriction_gc(struct key *keyring, extern void keyring_restriction_gc(struct key *keyring,
struct key_type *dead_type); struct key_type *dead_type);
void key_set_expiry(struct key *key, time64_t expiry);
extern void key_schedule_gc(time64_t gc_at); extern void key_schedule_gc(time64_t gc_at);
extern void key_schedule_gc_links(void); extern void key_schedule_gc_links(void);
extern void key_gc_keytype(struct key_type *ktype); extern void key_gc_keytype(struct key_type *ktype);
...@@ -215,10 +216,18 @@ extern struct key *key_get_instantiation_authkey(key_serial_t target_id); ...@@ -215,10 +216,18 @@ extern struct key *key_get_instantiation_authkey(key_serial_t target_id);
*/ */
static inline bool key_is_dead(const struct key *key, time64_t limit) static inline bool key_is_dead(const struct key *key, time64_t limit)
{ {
time64_t expiry = key->expiry;
if (expiry != TIME64_MAX) {
if (!(key->type->flags & KEY_TYPE_INSTANT_REAP))
expiry += key_gc_delay;
if (expiry <= limit)
return true;
}
return return
key->flags & ((1 << KEY_FLAG_DEAD) | key->flags & ((1 << KEY_FLAG_DEAD) |
(1 << KEY_FLAG_INVALIDATED)) || (1 << KEY_FLAG_INVALIDATED)) ||
(key->expiry > 0 && key->expiry <= limit) ||
key->domain_tag->removed; key->domain_tag->removed;
} }
......
...@@ -294,6 +294,7 @@ struct key *key_alloc(struct key_type *type, const char *desc, ...@@ -294,6 +294,7 @@ struct key *key_alloc(struct key_type *type, const char *desc,
key->uid = uid; key->uid = uid;
key->gid = gid; key->gid = gid;
key->perm = perm; key->perm = perm;
key->expiry = TIME64_MAX;
key->restrict_link = restrict_link; key->restrict_link = restrict_link;
key->last_used_at = ktime_get_real_seconds(); key->last_used_at = ktime_get_real_seconds();
...@@ -463,10 +464,7 @@ static int __key_instantiate_and_link(struct key *key, ...@@ -463,10 +464,7 @@ static int __key_instantiate_and_link(struct key *key,
if (authkey) if (authkey)
key_invalidate(authkey); key_invalidate(authkey);
if (prep->expiry != TIME64_MAX) { key_set_expiry(key, prep->expiry);
key->expiry = prep->expiry;
key_schedule_gc(prep->expiry + key_gc_delay);
}
} }
} }
...@@ -606,8 +604,7 @@ int key_reject_and_link(struct key *key, ...@@ -606,8 +604,7 @@ int key_reject_and_link(struct key *key,
atomic_inc(&key->user->nikeys); atomic_inc(&key->user->nikeys);
mark_key_instantiated(key, -error); mark_key_instantiated(key, -error);
notify_key(key, NOTIFY_KEY_INSTANTIATED, -error); notify_key(key, NOTIFY_KEY_INSTANTIATED, -error);
key->expiry = ktime_get_real_seconds() + timeout; key_set_expiry(key, ktime_get_real_seconds() + timeout);
key_schedule_gc(key->expiry + key_gc_delay);
if (test_and_clear_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags)) if (test_and_clear_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags))
awaken = 1; awaken = 1;
...@@ -723,16 +720,14 @@ struct key_type *key_type_lookup(const char *type) ...@@ -723,16 +720,14 @@ struct key_type *key_type_lookup(const char *type)
void key_set_timeout(struct key *key, unsigned timeout) void key_set_timeout(struct key *key, unsigned timeout)
{ {
time64_t expiry = 0; time64_t expiry = TIME64_MAX;
/* make the changes with the locks held to prevent races */ /* make the changes with the locks held to prevent races */
down_write(&key->sem); down_write(&key->sem);
if (timeout > 0) if (timeout > 0)
expiry = ktime_get_real_seconds() + timeout; expiry = ktime_get_real_seconds() + timeout;
key_set_expiry(key, expiry);
key->expiry = expiry;
key_schedule_gc(key->expiry + key_gc_delay);
up_write(&key->sem); up_write(&key->sem);
} }
......
...@@ -198,7 +198,7 @@ static int proc_keys_show(struct seq_file *m, void *v) ...@@ -198,7 +198,7 @@ static int proc_keys_show(struct seq_file *m, void *v)
/* come up with a suitable timeout value */ /* come up with a suitable timeout value */
expiry = READ_ONCE(key->expiry); expiry = READ_ONCE(key->expiry);
if (expiry == 0) { if (expiry == TIME64_MAX) {
memcpy(xbuf, "perm", 5); memcpy(xbuf, "perm", 5);
} else if (now >= expiry) { } else if (now >= expiry) {
memcpy(xbuf, "expd", 5); memcpy(xbuf, "expd", 5);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment