Skip to content

Commit 937fd40

Browse files
committed
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: Jeffrey Altman <[email protected]> * 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
2 parents 13b7344 + 39299bd commit 937fd40

File tree

7 files changed

+64
-37
lines changed

7 files changed

+64
-37
lines changed

fs/afs/dynroot.c

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ static int afs_probe_cell_name(struct dentry *dentry)
114114
struct afs_net *net = afs_d2net(dentry);
115115
const char *name = dentry->d_name.name;
116116
size_t len = dentry->d_name.len;
117+
char *result = NULL;
117118
int ret;
118119

119120
/* Names prefixed with a dot are R/W mounts. */
@@ -131,9 +132,22 @@ static int afs_probe_cell_name(struct dentry *dentry)
131132
}
132133

133134
ret = dns_query(net->net, "afsdb", name, len, "srv=1",
134-
NULL, NULL, false);
135-
if (ret == -ENODATA || ret == -ENOKEY)
135+
&result, NULL, false);
136+
if (ret == -ENODATA || ret == -ENOKEY || ret == 0)
136137
ret = -ENOENT;
138+
if (ret > 0 && ret >= sizeof(struct dns_server_list_v1_header)) {
139+
struct dns_server_list_v1_header *v1 = (void *)result;
140+
141+
if (v1->hdr.zero == 0 &&
142+
v1->hdr.content == DNS_PAYLOAD_IS_SERVER_LIST &&
143+
v1->hdr.version == 1 &&
144+
(v1->status != DNS_LOOKUP_GOOD &&
145+
v1->status != DNS_LOOKUP_GOOD_WITH_BAD))
146+
return -ENOENT;
147+
148+
}
149+
150+
kfree(result);
137151
return ret;
138152
}
139153

@@ -252,20 +266,9 @@ static int afs_dynroot_d_revalidate(struct dentry *dentry, unsigned int flags)
252266
return 1;
253267
}
254268

255-
/*
256-
* Allow the VFS to enquire as to whether a dentry should be unhashed (mustn't
257-
* sleep)
258-
* - called from dput() when d_count is going to 0.
259-
* - return 1 to request dentry be unhashed, 0 otherwise
260-
*/
261-
static int afs_dynroot_d_delete(const struct dentry *dentry)
262-
{
263-
return d_really_is_positive(dentry);
264-
}
265-
266269
const struct dentry_operations afs_dynroot_dentry_operations = {
267270
.d_revalidate = afs_dynroot_d_revalidate,
268-
.d_delete = afs_dynroot_d_delete,
271+
.d_delete = always_delete_dentry,
269272
.d_release = afs_d_release,
270273
.d_automount = afs_d_automount,
271274
};

include/linux/key-type.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ struct key_type {
7373

7474
unsigned int flags;
7575
#define KEY_TYPE_NET_DOMAIN 0x00000001 /* Keys of this type have a net namespace domain */
76+
#define KEY_TYPE_INSTANT_REAP 0x00000002 /* Keys of this type don't have a delay after expiring */
7677

7778
/* vet a description */
7879
int (*vet_description)(const char *description);

net/dns_resolver/dns_key.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ const struct cred *dns_resolver_cache;
9191
static int
9292
dns_resolver_preparse(struct key_preparsed_payload *prep)
9393
{
94+
const struct dns_server_list_v1_header *v1;
9495
const struct dns_payload_header *bin;
9596
struct user_key_payload *upayload;
9697
unsigned long derrno;
@@ -122,6 +123,13 @@ dns_resolver_preparse(struct key_preparsed_payload *prep)
122123
return -EINVAL;
123124
}
124125

126+
v1 = (const struct dns_server_list_v1_header *)bin;
127+
if ((v1->status != DNS_LOOKUP_GOOD &&
128+
v1->status != DNS_LOOKUP_GOOD_WITH_BAD)) {
129+
if (prep->expiry == TIME64_MAX)
130+
prep->expiry = ktime_get_real_seconds() + 1;
131+
}
132+
125133
result_len = datalen;
126134
goto store_result;
127135
}
@@ -314,7 +322,7 @@ static long dns_resolver_read(const struct key *key,
314322

315323
struct key_type key_type_dns_resolver = {
316324
.name = "dns_resolver",
317-
.flags = KEY_TYPE_NET_DOMAIN,
325+
.flags = KEY_TYPE_NET_DOMAIN | KEY_TYPE_INSTANT_REAP,
318326
.preparse = dns_resolver_preparse,
319327
.free_preparse = dns_resolver_free_preparse,
320328
.instantiate = generic_key_instantiate,

security/keys/gc.c

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,19 @@ void key_schedule_gc(time64_t gc_at)
6666
}
6767
}
6868

69+
/*
70+
* Set the expiration time on a key.
71+
*/
72+
void key_set_expiry(struct key *key, time64_t expiry)
73+
{
74+
key->expiry = expiry;
75+
if (expiry != TIME64_MAX) {
76+
if (!(key->type->flags & KEY_TYPE_INSTANT_REAP))
77+
expiry += key_gc_delay;
78+
key_schedule_gc(expiry);
79+
}
80+
}
81+
6982
/*
7083
* Schedule a dead links collection run.
7184
*/
@@ -176,29 +189,24 @@ static void key_garbage_collector(struct work_struct *work)
176189
static u8 gc_state; /* Internal persistent state */
177190
#define KEY_GC_REAP_AGAIN 0x01 /* - Need another cycle */
178191
#define KEY_GC_REAPING_LINKS 0x02 /* - We need to reap links */
179-
#define KEY_GC_SET_TIMER 0x04 /* - We need to restart the timer */
180192
#define KEY_GC_REAPING_DEAD_1 0x10 /* - We need to mark dead keys */
181193
#define KEY_GC_REAPING_DEAD_2 0x20 /* - We need to reap dead key links */
182194
#define KEY_GC_REAPING_DEAD_3 0x40 /* - We need to reap dead keys */
183195
#define KEY_GC_FOUND_DEAD_KEY 0x80 /* - We found at least one dead key */
184196

185197
struct rb_node *cursor;
186198
struct key *key;
187-
time64_t new_timer, limit;
199+
time64_t new_timer, limit, expiry;
188200

189201
kenter("[%lx,%x]", key_gc_flags, gc_state);
190202

191203
limit = ktime_get_real_seconds();
192-
if (limit > key_gc_delay)
193-
limit -= key_gc_delay;
194-
else
195-
limit = key_gc_delay;
196204

197205
/* Work out what we're going to be doing in this pass */
198206
gc_state &= KEY_GC_REAPING_DEAD_1 | KEY_GC_REAPING_DEAD_2;
199207
gc_state <<= 1;
200208
if (test_and_clear_bit(KEY_GC_KEY_EXPIRED, &key_gc_flags))
201-
gc_state |= KEY_GC_REAPING_LINKS | KEY_GC_SET_TIMER;
209+
gc_state |= KEY_GC_REAPING_LINKS;
202210

203211
if (test_and_clear_bit(KEY_GC_REAP_KEYTYPE, &key_gc_flags))
204212
gc_state |= KEY_GC_REAPING_DEAD_1;
@@ -233,8 +241,11 @@ static void key_garbage_collector(struct work_struct *work)
233241
}
234242
}
235243

236-
if (gc_state & KEY_GC_SET_TIMER) {
237-
if (key->expiry > limit && key->expiry < new_timer) {
244+
expiry = key->expiry;
245+
if (expiry != TIME64_MAX) {
246+
if (!(key->type->flags & KEY_TYPE_INSTANT_REAP))
247+
expiry += key_gc_delay;
248+
if (expiry > limit && expiry < new_timer) {
238249
kdebug("will expire %x in %lld",
239250
key_serial(key), key->expiry - limit);
240251
new_timer = key->expiry;
@@ -276,7 +287,7 @@ static void key_garbage_collector(struct work_struct *work)
276287
*/
277288
kdebug("pass complete");
278289

279-
if (gc_state & KEY_GC_SET_TIMER && new_timer != (time64_t)TIME64_MAX) {
290+
if (new_timer != TIME64_MAX) {
280291
new_timer += key_gc_delay;
281292
key_schedule_gc(new_timer);
282293
}

security/keys/internal.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ extern unsigned key_gc_delay;
167167
extern void keyring_gc(struct key *keyring, time64_t limit);
168168
extern void keyring_restriction_gc(struct key *keyring,
169169
struct key_type *dead_type);
170+
void key_set_expiry(struct key *key, time64_t expiry);
170171
extern void key_schedule_gc(time64_t gc_at);
171172
extern void key_schedule_gc_links(void);
172173
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);
215216
*/
216217
static inline bool key_is_dead(const struct key *key, time64_t limit)
217218
{
219+
time64_t expiry = key->expiry;
220+
221+
if (expiry != TIME64_MAX) {
222+
if (!(key->type->flags & KEY_TYPE_INSTANT_REAP))
223+
expiry += key_gc_delay;
224+
if (expiry <= limit)
225+
return true;
226+
}
227+
218228
return
219229
key->flags & ((1 << KEY_FLAG_DEAD) |
220230
(1 << KEY_FLAG_INVALIDATED)) ||
221-
(key->expiry > 0 && key->expiry <= limit) ||
222231
key->domain_tag->removed;
223232
}
224233

security/keys/key.c

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,7 @@ struct key *key_alloc(struct key_type *type, const char *desc,
294294
key->uid = uid;
295295
key->gid = gid;
296296
key->perm = perm;
297+
key->expiry = TIME64_MAX;
297298
key->restrict_link = restrict_link;
298299
key->last_used_at = ktime_get_real_seconds();
299300

@@ -463,10 +464,7 @@ static int __key_instantiate_and_link(struct key *key,
463464
if (authkey)
464465
key_invalidate(authkey);
465466

466-
if (prep->expiry != TIME64_MAX) {
467-
key->expiry = prep->expiry;
468-
key_schedule_gc(prep->expiry + key_gc_delay);
469-
}
467+
key_set_expiry(key, prep->expiry);
470468
}
471469
}
472470

@@ -606,8 +604,7 @@ int key_reject_and_link(struct key *key,
606604
atomic_inc(&key->user->nikeys);
607605
mark_key_instantiated(key, -error);
608606
notify_key(key, NOTIFY_KEY_INSTANTIATED, -error);
609-
key->expiry = ktime_get_real_seconds() + timeout;
610-
key_schedule_gc(key->expiry + key_gc_delay);
607+
key_set_expiry(key, ktime_get_real_seconds() + timeout);
611608

612609
if (test_and_clear_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags))
613610
awaken = 1;
@@ -723,16 +720,14 @@ struct key_type *key_type_lookup(const char *type)
723720

724721
void key_set_timeout(struct key *key, unsigned timeout)
725722
{
726-
time64_t expiry = 0;
723+
time64_t expiry = TIME64_MAX;
727724

728725
/* make the changes with the locks held to prevent races */
729726
down_write(&key->sem);
730727

731728
if (timeout > 0)
732729
expiry = ktime_get_real_seconds() + timeout;
733-
734-
key->expiry = expiry;
735-
key_schedule_gc(key->expiry + key_gc_delay);
730+
key_set_expiry(key, expiry);
736731

737732
up_write(&key->sem);
738733
}

security/keys/proc.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ static int proc_keys_show(struct seq_file *m, void *v)
198198

199199
/* come up with a suitable timeout value */
200200
expiry = READ_ONCE(key->expiry);
201-
if (expiry == 0) {
201+
if (expiry == TIME64_MAX) {
202202
memcpy(xbuf, "perm", 5);
203203
} else if (now >= expiry) {
204204
memcpy(xbuf, "expd", 5);

0 commit comments

Comments
 (0)