From stanislawa.laskowska at salesfusion.pl  Thu Jan  2 00:30:15 2025
From: stanislawa.laskowska at salesfusion.pl (=?UTF-8?Q? Stanis=C5=82awa_Laskowska ?=)
Date: Thu,  2 Jan 2025 08:30:15 GMT
Subject: Kandydat do pracy
Message-ID: <20250102084500-0.1.7.6ii.0.pc0am443mq@salesfusion.pl>

Dzie? dobry,

delegujemy sprawdzone osoby ze Wschodu do pracy w firmach produkcyjnych do prostych prac przy produkcji, na magazynie, przy pakowaniu towaru czy kontroli jako?ci. 

Nasza firma organizuje wszelkie formalno?ci oraz zakwaterowanie, co pozwala zmniejszy? koszty po Pa?stwa stronie.

Czy mog? przedstawi? szczeg??y?


Pozdrawiam
Stanis?awa Laskowska


From syzbot+7848fee1f1e5c53f912b at syzkaller.appspotmail.com  Thu Jan  2 09:52:29 2025
From: syzbot+7848fee1f1e5c53f912b at syzkaller.appspotmail.com (syzbot)
Date: Thu, 02 Jan 2025 09:52:29 -0800
Subject: [syzbot] [afs?] WARNING in __proc_create (3)
Message-ID: <6776d25d.050a0220.3a8527.0048.GAE@google.com>

Hello,

syzbot found the following issue on:

HEAD commit:    059dd502b263 Merge tag 'block-6.13-20241228' of git://git...
git tree:       upstream
console output: https://syzkaller.appspot.com/x/log.txt?x=125ac6df980000
kernel config:  https://syzkaller.appspot.com/x/.config?x=c078001e66e4a17e
dashboard link: https://syzkaller.appspot.com/bug?extid=7848fee1f1e5c53f912b
compiler:       gcc (Debian 12.2.0-14) 12.2.0, GNU ld (GNU Binutils for Debian) 2.40
syz repro:      https://syzkaller.appspot.com/x/repro.syz?x=165ac6df980000
C reproducer:   https://syzkaller.appspot.com/x/repro.c?x=1755b0b0580000

Downloadable assets:
disk image: https://storage.googleapis.com/syzbot-assets/b23b5d964e6e/disk-059dd502.raw.xz
vmlinux: https://storage.googleapis.com/syzbot-assets/6c382805ea80/vmlinux-059dd502.xz
kernel image: https://storage.googleapis.com/syzbot-assets/b17a5405f713/bzImage-059dd502.xz

IMPORTANT: if you fix the issue, please add the following tag to the commit:
Reported-by: syzbot+7848fee1f1e5c53f912b at syzkaller.appspotmail.com

------------[ cut here ]------------
name len 256
WARNING: CPU: 0 PID: 9 at fs/proc/generic.c:405 __proc_create+0x75c/0x8b0 fs/proc/generic.c:405
Modules linked in:
CPU: 0 UID: 0 PID: 9 Comm: kworker/0:1 Not tainted 6.13.0-rc4-syzkaller-00078-g059dd502b263 #0
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 09/13/2024
Workqueue: afs afs_manage_cell_work
RIP: 0010:__proc_create+0x75c/0x8b0 fs/proc/generic.c:405
Code: 80 3c 02 00 0f 85 61 01 00 00 4d 8b 3c 24 e9 17 fb ff ff e8 56 92 63 ff 90 8b 74 24 04 48 c7 c7 00 6a 61 8b e8 85 58 24 ff 90 <0f> 0b 90 90 e9 7b ff ff ff 4c 89 ef e8 a3 01 c6 ff e9 9e fa ff ff
RSP: 0018:ffffc900000e7a38 EFLAGS: 00010286
RAX: 0000000000000000 RBX: 1ffff9200001cf49 RCX: ffffffff815a1729
RDX: ffff88801bee8000 RSI: ffffffff815a1736 RDI: 0000000000000001
RBP: 0000000000000100 R08: 0000000000000001 R09: 0000000000000000
R10: 0000000000000001 R11: 0000000000000001 R12: ffffc900000e7b28
R13: ffff88802a3fa000 R14: 00000000000000ff R15: dffffc0000000000
FS:  0000000000000000(0000) GS:ffff8880b8600000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000000000000000 CR3: 000000002b2e2000 CR4: 00000000003526f0
DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
Call Trace:
 <TASK>
 _proc_mkdir+0xbb/0x200 fs/proc/generic.c:489
 proc_net_mkdir include/linux/proc_fs.h:232 [inline]
 afs_proc_cell_setup+0xbe/0x200 fs/afs/proc.c:682
 afs_activate_cell fs/afs/cell.c:681 [inline]
 afs_manage_cell fs/afs/cell.c:761 [inline]
 afs_manage_cell_work+0x614/0x14b0 fs/afs/cell.c:828
 process_one_work+0x958/0x1b30 kernel/workqueue.c:3229
 process_scheduled_works kernel/workqueue.c:3310 [inline]
 worker_thread+0x6c8/0xf00 kernel/workqueue.c:3391
 kthread+0x2c1/0x3a0 kernel/kthread.c:389
 ret_from_fork+0x45/0x80 arch/x86/kernel/process.c:147
 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:244
 </TASK>


---
This report is generated by a bot. It may contain errors.
See https://goo.gl/tpsmEJ for more information about syzbot.
syzbot engineers can be reached at syzkaller at googlegroups.com.

syzbot will keep track of this issue. See:
https://goo.gl/tpsmEJ#status for how to communicate with syzbot.

If the report is already addressed, let syzbot know by replying with:
#syz fix: exact-commit-title

If you want syzbot to run the reproducer, reply with:
#syz test: git://repo/address.git branch-or-commit-hash
If you attach or paste a git patch, syzbot will apply it before testing.

If you want to overwrite report's subsystems, reply with:
#syz set subsystems: new-subsystem
(See the list of subsystem names on the web dashboard)

If the report is a duplicate of another one, reply with:
#syz dup: exact-subject-of-another-report

If you want to undo deduplication, reply with:
#syz undup


From dhowells at redhat.com  Mon Jan  6 05:48:35 2025
From: dhowells at redhat.com (David Howells)
Date: Mon, 06 Jan 2025 13:48:35 +0000
Subject: [PATCH] afs: Merge preference rule failure condition
In-Reply-To: <20241226012616.2348907-1-lizhi.xu@windriver.com>
References: <20241226012616.2348907-1-lizhi.xu@windriver.com> <676c7f0d.050a0220.2f3838.03bb.GAE@google.com>
Message-ID: <291912.1736171315@warthog.procyon.org.uk>

Lizhi Xu <lizhi.xu at windriver.com> wrote:

>  		argc = afs_split_string(&buf, argv, ARRAY_SIZE(argv));
> -		if (argc < 0)
> -			return argc;
>  		if (argc < 2)
>  			goto inval;

I think this needs to be slightly different.  afs_split_string() will print
error messages and can return an error code, so we should go with that and set
ret to argc and go to done, not inval.

David



From dhowells at redhat.com  Mon Jan  6 06:06:51 2025
From: dhowells at redhat.com (David Howells)
Date: Mon, 06 Jan 2025 14:06:51 +0000
Subject: [syzbot] [afs?] WARNING: lock held when returning to user space in afs_proc_addr_prefs_write
In-Reply-To: <676c7f0d.050a0220.2f3838.03bb.GAE@google.com>
References: <676c7f0d.050a0220.2f3838.03bb.GAE@google.com>
Message-ID: <363219.1736172411@warthog.procyon.org.uk>

#syz test: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git master

afs: Merge preference rule failure condition

syzbot reported a lock held when returning to userspace[1].  This is
because if argc is less than 0 and the function returns directly, the held
inode lock is not released.

Fix this by store the error in ret and jump to done to clean up instead of
returning directly.

[dh: Modified Lizhi Xu's original patch to make it honour the error code
from afs_split_string()]

[1]
WARNING: lock held when returning to user space!
6.13.0-rc3-syzkaller-00209-g499551201b5f #0 Not tainted
------------------------------------------------
syz-executor133/5823 is leaving the kernel with locks still held!
1 lock held by syz-executor133/5823:
 #0: ffff888071cffc00 (&sb->s_type->i_mutex_key#9){++++}-{4:4}, at: inode_lock include/linux/fs.h:818 [inline]
 #0: ffff888071cffc00 (&sb->s_type->i_mutex_key#9){++++}-{4:4}, at: afs_proc_addr_prefs_write+0x2bb/0x14e0 fs/afs/addr_prefs.c:388

Reported-by: syzbot+76f33569875eb708e575 at syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=76f33569875eb708e575
Signed-off-by: Lizhi Xu <lizhi.xu at windriver.com>
Signed-off-by: David Howells <dhowells at redhat.com>
cc: Marc Dionne <marc.dionne at auristor.com>
cc: linux-afs at lists.infradead.org
Link: https://lore.kernel.org/r/20241226012616.2348907-1-lizhi.xu at windriver.com/
---
diff --git a/fs/afs/addr_prefs.c b/fs/afs/addr_prefs.c
index a189ff8a5034..c0384201b8fe 100644
--- a/fs/afs/addr_prefs.c
+++ b/fs/afs/addr_prefs.c
@@ -413,8 +413,10 @@ int afs_proc_addr_prefs_write(struct file *file, char *buf, size_t size)
 
 	do {
 		argc = afs_split_string(&buf, argv, ARRAY_SIZE(argv));
-		if (argc < 0)
-			return argc;
+		if (argc < 0) {
+			ret = argc;
+			goto done;
+		}
 		if (argc < 2)
 			goto inval;
 


From syzbot+76f33569875eb708e575 at syzkaller.appspotmail.com  Mon Jan  6 06:27:03 2025
From: syzbot+76f33569875eb708e575 at syzkaller.appspotmail.com (syzbot)
Date: Mon, 06 Jan 2025 06:27:03 -0800
Subject: [syzbot] [afs?] WARNING: lock held when returning to user space
 in afs_proc_addr_prefs_write
In-Reply-To: <363219.1736172411@warthog.procyon.org.uk>
Message-ID: <677be837.050a0220.3b3668.000d.GAE@google.com>

Hello,

syzbot tried to test the proposed patch but the build/boot failed:

 page owner found early allocated 19843 pages
[    2.071960][    T0] Kernel/User page tables isolation: enabled
[    2.075149][    T0] Dynamic Preempt: full
[    2.077132][    T0] Running RCU self tests
[    2.078106][    T0] Running RCU synchronous self tests
[    2.079907][    T0] rcu: Preemptible hierarchical RCU implementation.
[    2.081445][    T0] rcu: 	RCU lockdep checking is enabled.
[    2.083588][    T0] rcu: 	RCU restricting CPUs from NR_CPUS=8 to nr_cpu_ids=2.
[    2.085399][    T0] rcu: 	RCU callback double-/use-after-free debug is enabled.
[    2.087450][    T0] rcu: 	RCU debug extended QS entry/exit.
[    2.088523][    T0] 	All grace periods are expedited (rcu_expedited).
[    2.090346][    T0] 	Trampoline variant of Tasks RCU enabled.
[    2.091376][    T0] 	Tracing variant of Tasks RCU enabled.
[    2.092797][    T0] rcu: RCU calculated value of scheduler-enlistment delay is 10 jiffies.
[    2.094823][    T0] rcu: Adjusting geometry for rcu_fanout_leaf=16, nr_cpu_ids=2
[    2.096481][    T0] Running RCU synchronous self tests
[    2.097771][    T0] RCU Tasks: Setting shift to 1 and lim to 1 rcu_task_cb_adjust=1 rcu_task_cpu_ids=2.
[    2.099560][    T0] RCU Tasks Trace: Setting shift to 1 and lim to 1 rcu_task_cb_adjust=1 rcu_task_cpu_ids=2.
[    2.247806][    T0] NR_IRQS: 4352, nr_irqs: 440, preallocated irqs: 16
[    2.250263][    T0] rcu: srcu_init: Setting srcu_struct sizes based on contention.
[    2.252270][    T0] kfence: initialized - using 2097152 bytes for 255 objects at 0xffff88823be00000-0xffff88823c000000
[    2.255249][    T0] Console: colour VGA+ 80x25
[    2.256184][    T0] printk: legacy console [ttyS0] enabled
[    2.256184][    T0] printk: legacy console [ttyS0] enabled
[    2.258337][    T0] printk: legacy bootconsole [earlyser0] disabled
[    2.258337][    T0] printk: legacy bootconsole [earlyser0] disabled
[    2.260621][    T0] Lock dependency validator: Copyright (c) 2006 Red Hat, Inc., Ingo Molnar
[    2.262203][    T0] ... MAX_LOCKDEP_SUBCLASSES:  8
[    2.262945][    T0] ... MAX_LOCK_DEPTH:          48
[    2.263988][    T0] ... MAX_LOCKDEP_KEYS:        8192
[    2.265553][    T0] ... CLASSHASH_SIZE:          4096
[    2.267023][    T0] ... MAX_LOCKDEP_ENTRIES:     1048576
[    2.267977][    T0] ... MAX_LOCKDEP_CHAINS:      1048576
[    2.268789][    T0] ... CHAINHASH_SIZE:          524288
[    2.269562][    T0]  memory used by lock dependency info: 106625 kB
[    2.270554][    T0]  memory used for stack traces: 8320 kB
[    2.271461][    T0]  per task-struct memory footprint: 1920 bytes
[    2.273329][    T0] mempolicy: Enabling automatic NUMA balancing. Configure with numa_balancing= or the kernel.numa_balancing sysctl
[    2.275805][    T0] ACPI: Core revision 20240827
[    2.277665][    T0] APIC: Switch to symmetric I/O mode setup
[    2.279420][    T0] x2apic enabled
[    2.284009][    T0] APIC: Switched APIC routing to: physical x2apic
[    2.291502][    T0] ..TIMER: vector=0x30 apic1=0 pin1=0 apic2=-1 pin2=-1
[    2.294252][    T0] clocksource: tsc-early: mask: 0xffffffffffffffff max_cycles: 0x1fb6f965d9b, max_idle_ns: 440795282877 ns
[    2.298627][    T0] Calibrating delay loop (skipped) preset value.. 4400.42 BogoMIPS (lpj=22002100)
[    2.301057][    T0] Last level iTLB entries: 4KB 64, 2MB 8, 4MB 8
[    2.302645][    T0] Last level dTLB entries: 4KB 64, 2MB 0, 4MB 0, 1GB 4
[    2.304079][    T0] Spectre V1 : Mitigation: usercopy/swapgs barriers and __user pointer sanitization
[    2.305738][    T0] Spectre V2 : Spectre BHI mitigation: SW BHB clearing on syscall and VM exit
[    2.307226][    T0] Spectre V2 : Mitigation: IBRS
[    2.308628][    T0] Spectre V2 : Spectre v2 / SpectreRSB mitigation: Filling RSB on context switch
[    2.310524][    T0] Spectre V2 : Spectre v2 / SpectreRSB : Filling RSB on VMEXIT
[    2.311803][    T0] RETBleed: Mitigation: IBRS
[    2.312526][    T0] Spectre V2 : mitigation: Enabling conditional Indirect Branch Prediction Barrier
[    2.313856][    T0] Spectre V2 : User space: Mitigation: STIBP via prctl
[    2.315073][    T0] Speculative Store Bypass: Mitigation: Speculative Store Bypass disabled via prctl
[    2.316603][    T0] MDS: Mitigation: Clear CPU buffers
[    2.317473][    T0] TAA: Mitigation: Clear CPU buffers
[    2.318622][    T0] MMIO Stale Data: Vulnerable: Clear CPU buffers attempted, no microcode
[    2.320919][    T0] x86/fpu: Supporting XSAVE feature 0x001: 'x87 floating point registers'
[    2.322382][    T0] x86/fpu: Supporting XSAVE feature 0x002: 'SSE registers'
[    2.323635][    T0] x86/fpu: Supporting XSAVE feature 0x004: 'AVX registers'
[    2.324822][    T0] x86/fpu: xstate_offset[2]:  576, xstate_sizes[2]:  256
[    2.325946][    T0] x86/fpu: Enabled xstate features 0x7, context size is 832 bytes, using 'standard' format.
[    2.592952][    T0] Freeing SMP alternatives memory: 124K
[    2.594208][    T0] pid_max: default: 32768 minimum: 301
[    2.595787][    T0] LSM: initializing lsm=lockdown,capability,landlock,yama,safesetid,tomoyo,apparmor,bpf,ima,evm
[    2.598213][    T0] landlock: Up and running.
[    2.598628][    T0] Yama: becoming mindful.
[    2.599717][    T0] TOMOYO Linux initialized
[    2.601324][    T0] AppArmor: AppArmor initialized
[    2.604240][    T0] LSM support for eBPF active
[    2.611042][    T0] Dentry cache hash table entries: 1048576 (order: 11, 8388608 bytes, vmalloc hugepage)
[    2.615317][    T0] Inode-cache hash table entries: 524288 (order: 10, 4194304 bytes, vmalloc hugepage)
[    2.617873][    T0] Mount-cache hash table entries: 16384 (order: 5, 131072 bytes, vmalloc)
[    2.618918][    T0] Mountpoint-cache hash table entries: 16384 (order: 5, 131072 bytes, vmalloc)
[    2.624503][    T0] Running RCU synchronous self tests
[    2.625906][    T0] Running RCU synchronous self tests
[    2.750053][    T1] smpboot: CPU0: Intel(R) Xeon(R) CPU @ 2.20GHz (family: 0x6, model: 0x4f, stepping: 0x0)
[    2.756899][    T1] Running RCU Tasks wait API self tests
[    2.859036][    T1] Running RCU Tasks Trace wait API self tests
[    2.860455][    T1] Performance Events: unsupported p6 CPU model 79 no PMU driver, software events only.
[    2.862568][    T1] signal: max sigframe size: 1776
[    2.864222][    T1] rcu: Hierarchical SRCU implementation.
[    2.865500][    T1] rcu: 	Max phase no-delay instances is 1000.
[    2.867279][    T1] Timer migration: 1 hierarchy levels; 8 children per group; 0 crossnode level
[    2.873416][    T1] NMI watchdog: Perf NMI watchdog permanently disabled
[    2.875521][    T1] smp: Bringing up secondary CPUs ...
[    2.878815][    T1] smpboot: x86: Booting SMP configuration:
[    2.880304][    T1] .... node  #0, CPUs:      #1
[    2.880519][   T15] Callback from call_rcu_tasks_trace() invoked.
[    2.883042][   T22] ------------[ cut here ]------------
[    2.883042][   T22] workqueue: work disable count underflowed
[    2.883042][   T22] WARNING: CPU: 1 PID: 22 at kernel/workqueue.c:4317 enable_work+0x2fa/0x340
[    2.883042][   T22] Modules linked in:
[    2.883554][   T22] CPU: 1 UID: 0 PID: 22 Comm: cpuhp/1 Not tainted 6.13.0-rc6-syzkaller-g9d89551994a4-dirty #0
[    2.885161][   T22] Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 09/13/2024
[    2.887278][   T22] RIP: 0010:enable_work+0x2fa/0x340
[    2.888264][   T22] Code: 89 ee e8 e9 59 36 00 45 84 ed 0f 85 28 fe ff ff e8 3b 5f 36 00 c6 05 c9 96 a2 0e 01 90 48 c7 c7 20 d3 4b 8b e8 a7 22 f7 ff 90 <0f> 0b 90 90 e9 05 fe ff ff 48 89 ef e8 85 c7 98 00 e9 a9 fe ff ff
[    2.888609][   T22] RSP: 0000:ffffc900001c7ca0 EFLAGS: 00010082
[    2.888609][   T22] RAX: 0000000000000000 RBX: 0000000000000000 RCX: ffffffff815a1789
[    2.888609][   T22] RDX: ffff88801d2f3c00 RSI: ffffffff815a1796 RDI: 0000000000000001
[    2.888609][   T22] RBP: ffff8880b8738660 R08: 0000000000000001 R09: 0000000000000000
[    2.888609][   T22] R10: 0000000000000000 R11: 0000000000000001 R12: 1ffff92000038f95
[    2.888609][   T22] R13: 0000000000000000 R14: 00000000000000c4 R15: ffffffff81db22a0
[    2.888609][   T22] FS:  0000000000000000(0000) GS:ffff8880b8700000(0000) knlGS:0000000000000000
[    2.888609][   T22] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[    2.888609][   T22] CR2: 0000000000000000 CR3: 000000000db7e000 CR4: 00000000003506f0
[    2.888609][   T22] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
[    2.888609][   T22] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
[    2.888609][   T22] Call Trace:
[    2.888609][   T22]  <TASK>
[    2.888609][   T22]  ? __warn+0xea/0x3c0
[    2.888609][   T22]  ? enable_work+0x2fa/0x340
[    2.888609][   T22]  ? report_bug+0x3c0/0x580
[    2.888609][   T22]  ? handle_bug+0x54/0xa0
[    2.888609][   T22]  ? exc_invalid_op+0x17/0x50
[    2.888609][   T22]  ? asm_exc_invalid_op+0x1a/0x20
[    2.888609][   T22]  ? __pfx_vmstat_cpu_online+0x10/0x10
[    2.888609][   T22]  ? __warn_printk+0x199/0x350
[    2.888609][   T22]  ? __warn_printk+0x1a6/0x350
[    2.888609][   T22]  ? enable_work+0x2fa/0x340
[    2.888609][   T22]  ? __pfx_enable_work+0x10/0x10
[    2.888609][   T22]  vmstat_cpu_online+0x83/0xf0
[    2.888609][   T22]  cpuhp_invoke_callback+0x3d0/0xa10
[    2.888609][   T22]  ? __pfx_vmstat_cpu_online+0x10/0x10
[    2.888609][   T22]  ? lock_acquire.part.0+0x2e0/0x380
[    2.888609][   T22]  ? cpuhp_next_state+0x100/0x1c0
[    2.888609][   T22]  cpuhp_thread_fun+0x480/0x6f0
[    2.888609][   T22]  ? __pfx_cpuhp_thread_fun+0x10/0x10
[    2.888609][   T22]  ? __pfx_cpuhp_thread_fun+0x10/0x10
[    2.888609][   T22]  ? smpboot_thread_fn+0x59d/0xa30
[    2.888609][   T22]  smpboot_thread_fn+0x661/0xa30
[    2.888609][   T22]  ? __kthread_parkme+0x148/0x220
[    2.888609][   T22]  ? __pfx_smpboot_thread_fn+0x10/0x10
[    2.888609][   T22]  kthread+0x2c1/0x3a0
[    2.888609][   T22]  ? _raw_spin_unlock_irq+0x23/0x50
[    2.888609][   T22]  ? __pfx_kthread+0x10/0x10
[    2.888609][   T22]  ret_from_fork+0x45/0x80
[    2.888609][   T22]  ? __pfx_kthread+0x10/0x10
[    2.888609][   T22]  ret_from_fork_asm+0x1a/0x30
[    2.888609][   T22]  </TASK>
[    2.888609][   T22] Kernel panic - not syncing: kernel: panic_on_warn set ...
[    2.888609][   T22] CPU: 1 UID: 0 PID: 22 Comm: cpuhp/1 Not tainted 6.13.0-rc6-syzkaller-g9d89551994a4-dirty #0
[    2.888609][   T22] Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 09/13/2024
[    2.888609][   T22] Call Trace:
[    2.888609][   T22]  <TASK>
[    2.888609][   T22]  dump_stack_lvl+0x3d/0x1f0
[    2.888609][   T22]  panic+0x71d/0x800
[    2.888609][   T22]  ? __pfx_panic+0x10/0x10
[    2.888609][   T22]  ? show_trace_log_lvl+0x29d/0x3d0
[    2.888609][   T22]  ? check_panic_on_warn+0x1f/0xb0
[    2.888609][   T22]  ? enable_work+0x2fa/0x340
[    2.888609][   T22]  check_panic_on_warn+0xab/0xb0
[    2.888609][   T22]  __warn+0xf6/0x3c0
[    2.888609][   T22]  ? enable_work+0x2fa/0x340
[    2.888609][   T22]  report_bug+0x3c0/0x580
[    2.888609][   T22]  handle_bug+0x54/0xa0
[    2.888609][   T22]  exc_invalid_op+0x17/0x50
[    2.888609][   T22]  asm_exc_invalid_op+0x1a/0x20
[    2.888609][   T22] RIP: 0010:enable_work+0x2fa/0x340
[    2.888609][   T22] Code: 89 ee e8 e9 59 36 00 45 84 ed 0f 85 28 fe ff ff e8 3b 5f 36 00 c6 05 c9 96 a2 0e 01 90 48 c7 c7 20 d3 4b 8b e8 a7 22 f7 ff 90 <0f> 0b 90 90 e9 05 fe ff ff 48 89 ef e8 85 c7 98 00 e9 a9 fe ff ff
[    2.888609][   T22] RSP: 0000:ffffc900001c7ca0 EFLAGS: 00010082
[    2.888609][   T22] RAX: 0000000000000000 RBX: 0000000000000000 RCX: ffffffff815a1789
[    2.888609][   T22] RDX: ffff88801d2f3c00 RSI: ffffffff815a1796 RDI: 0000000000000001
[    2.888609][   T22] RBP: ffff8880b8738660 R08: 0000000000000001 R09: 0000000000000000
[    2.888609][   T22] R10: 0000000000000000 R11: 0000000000000001 R12: 1ffff92000038f95
[    2.888609][   T22] R13: 0000000000000000 R14: 00000000000000c4 R15: ffffffff81db22a0
[    2.888609][   T22]  ? __pfx_vmstat_cpu_online+0x10/0x10
[    2.888609][   T22]  ? __warn_printk+0x199/0x350
[    2.888609][   T22]  ? __warn_printk+0x1a6/0x350
[    2.888609][   T22]  ? __pfx_enable_work+0x10/0x10
[    2.888609][   T22]  vmstat_cpu_online+0x83/0xf0
[    2.888609][   T22]  cpuhp_invoke_callback+0x3d0/0xa10
[    2.888609][   T22]  ? __pfx_vmstat_cpu_online+0x10/0x10
[    2.888609][   T22]  ? lock_acquire.part.0+0x2e0/0x380
[    2.888609][   T22]  ? cpuhp_next_state+0x100/0x1c0
[    2.888609][   T22]  cpuhp_thread_fun+0x480/0x6f0
[    2.888609][   T22]  ? __pfx_cpuhp_thread_fun+0x10/0x10
[    2.888609][   T22]  ? __pfx_cpuhp_thread_fun+0x10/0x10
[    2.888609][   T22]  ? smpboot_thread_fn+0x59d/0xa30
[    2.888609][   T22]  smpboot_thread_fn+0x661/0xa30
[    2.888609][   T22]  ? __kthread_parkme+0x148/0x220
[    2.888609][   T22]  ? __pfx_smpboot_thread_fn+0x10/0x10
[    2.888609][   T22]  kthread+0x2c1/0x3a0
[    2.888609][   T22]  ? _raw_spin_unlock_irq+0x23/0x50
[    2.888609][   T22]  ? __pfx_kthread+0x10/0x10
[    2.888609][   T22]  ret_from_fork+0x45/0x80
[    2.888609][   T22]  ? __pfx_kthread+0x10/0x10
[    2.888609][   T22]  ret_from_fork_asm+0x1a/0x30
[    2.888609][   T22]  </TASK>
[    2.888609][   T22] Rebooting in 86400 seconds..


syzkaller build log:
go env (err=<nil>)
GO111MODULE='auto'
GOARCH='amd64'
GOBIN=''
GOCACHE='/syzkaller/.cache/go-build'
GOENV='/syzkaller/.config/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOHOSTARCH='amd64'
GOHOSTOS='linux'
GOINSECURE=''
GOMODCACHE='/syzkaller/jobs/linux/gopath/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='linux'
GOPATH='/syzkaller/jobs/linux/gopath'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/usr/local/go'
GOSUMDB='sum.golang.org'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/usr/local/go/pkg/tool/linux_amd64'
GOVCS=''
GOVERSION='go1.22.7'
GCCGO='gccgo'
GOAMD64='v1'
AR='ar'
CC='gcc'
CXX='g++'
CGO_ENABLED='1'
GOMOD='/syzkaller/jobs/linux/gopath/src/github.com/google/syzkaller/go.mod'
GOWORK=''
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
PKG_CONFIG='pkg-config'
GOGCCFLAGS='-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build1808338263=/tmp/go-build -gno-record-gcc-switches'

git status (err=<nil>)
HEAD detached at d7f584ee3c
nothing to commit, working tree clean


tput: No value for $TERM and no -T specified
tput: No value for $TERM and no -T specified
Makefile:31: run command via tools/syz-env for best compatibility, see:
Makefile:32: https://github.com/google/syzkaller/blob/master/docs/contributing.md#using-syz-env
go list -f '{{.Stale}}' ./sys/syz-sysgen | grep -q false || go install ./sys/syz-sysgen
make .descriptions
tput: No value for $TERM and no -T specified
tput: No value for $TERM and no -T specified
Makefile:31: run command via tools/syz-env for best compatibility, see:
Makefile:32: https://github.com/google/syzkaller/blob/master/docs/contributing.md#using-syz-env
bin/syz-sysgen
go fmt ./sys/... >/dev/null
touch .descriptions
GOOS=linux GOARCH=amd64 go build "-ldflags=-s -w -X github.com/google/syzkaller/prog.GitRevision=d7f584ee3c24504bb07d04526a23b7d8df38b8ed -X 'github.com/google/syzkaller/prog.gitRevisionDate=20241220-210006'" "-tags=syz_target syz_os_linux syz_arch_amd64 " -o ./bin/linux_amd64/syz-execprog github.com/google/syzkaller/tools/syz-execprog
mkdir -p ./bin/linux_amd64
g++ -o ./bin/linux_amd64/syz-executor executor/executor.cc \
	-m64 -O2 -pthread -Wall -Werror -Wparentheses -Wunused-const-variable -Wframe-larger-than=16384 -Wno-stringop-overflow -Wno-array-bounds -Wno-format-overflow -Wno-unused-but-set-variable -Wno-unused-command-line-argument -static-pie -std=c++17 -I. -Iexecutor/_include -fpermissive -w -DGOOS_linux=1 -DGOARCH_amd64=1 \
	-DHOSTGOOS_linux=1 -DGIT_REVISION=\"d7f584ee3c24504bb07d04526a23b7d8df38b8ed\"
/usr/bin/ld: /tmp/ccNZxct1.o: in function `Connection::Connect(char const*, char const*)':
executor.cc:(.text._ZN10Connection7ConnectEPKcS1_[_ZN10Connection7ConnectEPKcS1_]+0x104): warning: Using 'gethostbyname' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking


Error text is too large and was truncated, full error text is at:
https://syzkaller.appspot.com/x/error.txt?x=121736f8580000


Tested on:

commit:         9d895519 Linux 6.13-rc6
git tree:       upstream
kernel config:  https://syzkaller.appspot.com/x/.config?x=ad08f7f48e13abcd
dashboard link: https://syzkaller.appspot.com/bug?extid=76f33569875eb708e575
compiler:       gcc (Debian 12.2.0-14) 12.2.0, GNU ld (GNU Binutils for Debian) 2.40
patch:          https://syzkaller.appspot.com/x/patch.diff?x=11671edf980000



From dhowells at redhat.com  Mon Jan  6 07:18:06 2025
From: dhowells at redhat.com (David Howells)
Date: Mon, 06 Jan 2025 15:18:06 +0000
Subject: [syzbot] [afs?] WARNING in __proc_create (3)
In-Reply-To: <6776d25d.050a0220.3a8527.0048.GAE@google.com>
References: <6776d25d.050a0220.3a8527.0048.GAE@google.com>
Message-ID: <373722.1736176686@warthog.procyon.org.uk>

#syz test: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git master

afs: Fix the maximum cell name length

The kafs filesystem limits the maximum length of a cell to 256 bytes, but a
problem occurs if someone actually does that: kafs tries to create a
directory under /proc/net/afs/ with the name of the cell, but that fails
with a warning:

        WARNING: CPU: 0 PID: 9 at fs/proc/generic.c:405

because procfs limits the maximum filename length to 255.

However, the DNS limits the maximum lookup length and, by extension, the
maximum cell name, to 255 less two (length count and trailing NUL).

Fix this by limiting the maximum acceptable cellname length to 253.  This
also allows us to be sure we can create the "/afs/.<cell>/" mountpoint too.

Further, split the YFS VL record cell name maximum to be the 256 allowed by
the protocol and ignore the record retrieved by YFSVL.GetCellName if it
exceeds 253.

Fixes: c3e9f888263b ("afs: Implement client support for the YFSVL.GetCellName RPC op")
Reported-by: syzbot+7848fee1f1e5c53f912b at syzkaller.appspotmail.com
Link: https://lore.kernel.org/r/6776d25d.050a0220.3a8527.0048.GAE at google.com/
Signed-off-by: David Howells <dhowells at redhat.com>
cc: Marc Dionne <marc.dionne at auristor.com>
cc: linux-afs at lists.infradead.org
---
 fs/afs/afs.h      |    2 +-
 fs/afs/afs_vl.h   |    1 +
 fs/afs/vl_alias.c |    8 ++++++--
 fs/afs/vlclient.c |    2 +-
 4 files changed, 9 insertions(+), 4 deletions(-)

diff --git a/fs/afs/afs.h b/fs/afs/afs.h
index b488072aee87..ec3db00bd081 100644
--- a/fs/afs/afs.h
+++ b/fs/afs/afs.h
@@ -10,7 +10,7 @@
 
 #include <linux/in.h>
 
-#define AFS_MAXCELLNAME		256  	/* Maximum length of a cell name */
+#define AFS_MAXCELLNAME		253  	/* Maximum length of a cell name (DNS limited) */
 #define AFS_MAXVOLNAME		64  	/* Maximum length of a volume name */
 #define AFS_MAXNSERVERS		8   	/* Maximum servers in a basic volume record */
 #define AFS_NMAXNSERVERS	13  	/* Maximum servers in a N/U-class volume record */
diff --git a/fs/afs/afs_vl.h b/fs/afs/afs_vl.h
index a06296c8827d..b835e25a2c02 100644
--- a/fs/afs/afs_vl.h
+++ b/fs/afs/afs_vl.h
@@ -13,6 +13,7 @@
 #define AFS_VL_PORT		7003	/* volume location service port */
 #define VL_SERVICE		52	/* RxRPC service ID for the Volume Location service */
 #define YFS_VL_SERVICE		2503	/* Service ID for AuriStor upgraded VL service */
+#define YFS_VL_MAXCELLNAME	256  	/* Maximum length of a cell name in YFS protocol */
 
 enum AFSVL_Operations {
 	VLGETENTRYBYID		= 503,	/* AFS Get VLDB entry by ID */
diff --git a/fs/afs/vl_alias.c b/fs/afs/vl_alias.c
index 9f36e14f1c2d..f9e76b604f31 100644
--- a/fs/afs/vl_alias.c
+++ b/fs/afs/vl_alias.c
@@ -253,6 +253,7 @@ static char *afs_vl_get_cell_name(struct afs_cell *cell, struct key *key)
 static int yfs_check_canonical_cell_name(struct afs_cell *cell, struct key *key)
 {
 	struct afs_cell *master;
+	size_t name_len;
 	char *cell_name;
 
 	cell_name = afs_vl_get_cell_name(cell, key);
@@ -264,8 +265,11 @@ static int yfs_check_canonical_cell_name(struct afs_cell *cell, struct key *key)
 		return 0;
 	}
 
-	master = afs_lookup_cell(cell->net, cell_name, strlen(cell_name),
-				 NULL, false);
+	name_len = strlen(cell_name);
+	if (!name_len || name_len > AFS_MAXCELLNAME)
+		master = ERR_PTR(-EOPNOTSUPP);
+	else
+		master = afs_lookup_cell(cell->net, cell_name, name_len, NULL, false);
 	kfree(cell_name);
 	if (IS_ERR(master))
 		return PTR_ERR(master);
diff --git a/fs/afs/vlclient.c b/fs/afs/vlclient.c
index cac75f89b64a..55dd0fc5aad7 100644
--- a/fs/afs/vlclient.c
+++ b/fs/afs/vlclient.c
@@ -697,7 +697,7 @@ static int afs_deliver_yfsvl_get_cell_name(struct afs_call *call)
 			return ret;
 
 		namesz = ntohl(call->tmp);
-		if (namesz > AFS_MAXCELLNAME)
+		if (namesz > YFS_VL_MAXCELLNAME)
 			return afs_protocol_error(call, afs_eproto_cellname_len);
 		paddedsz = (namesz + 3) & ~3;
 		call->count = namesz;



From syzbot+7848fee1f1e5c53f912b at syzkaller.appspotmail.com  Mon Jan  6 07:41:03 2025
From: syzbot+7848fee1f1e5c53f912b at syzkaller.appspotmail.com (syzbot)
Date: Mon, 06 Jan 2025 07:41:03 -0800
Subject: [syzbot] [afs?] WARNING in __proc_create (3)
In-Reply-To: <373722.1736176686@warthog.procyon.org.uk>
Message-ID: <677bf98f.050a0220.a40f5.0010.GAE@google.com>

Hello,

syzbot has tested the proposed patch and the reproducer did not trigger any issue:

Reported-by: syzbot+7848fee1f1e5c53f912b at syzkaller.appspotmail.com
Tested-by: syzbot+7848fee1f1e5c53f912b at syzkaller.appspotmail.com

Tested on:

commit:         13563da6 Merge tag 'vfio-v6.13-rc7' of https://github...
git tree:       upstream
console output: https://syzkaller.appspot.com/x/log.txt?x=173cb418580000
kernel config:  https://syzkaller.appspot.com/x/.config?x=ad08f7f48e13abcd
dashboard link: https://syzkaller.appspot.com/bug?extid=7848fee1f1e5c53f912b
compiler:       gcc (Debian 12.2.0-14) 12.2.0, GNU ld (GNU Binutils for Debian) 2.40
patch:          https://syzkaller.appspot.com/x/patch.diff?x=122cb418580000

Note: testing is done by a robot and is best-effort only.


From dhowells at redhat.com  Mon Jan  6 08:21:00 2025
From: dhowells at redhat.com (David Howells)
Date: Mon, 06 Jan 2025 16:21:00 +0000
Subject: [PATCH] afs: Fix the maximum cell name length 
Message-ID: <376236.1736180460@warthog.procyon.org.uk>

The kafs filesystem limits the maximum length of a cell to 256 bytes, but a
problem occurs if someone actually does that: kafs tries to create a
directory under /proc/net/afs/ with the name of the cell, but that fails
with a warning:

        WARNING: CPU: 0 PID: 9 at fs/proc/generic.c:405

because procfs limits the maximum filename length to 255.

However, the DNS limits the maximum lookup length and, by extension, the
maximum cell name, to 255 less two (length count and trailing NUL).

Fix this by limiting the maximum acceptable cellname length to 253.  This
also allows us to be sure we can create the "/afs/.<cell>/" mountpoint too.

Further, split the YFS VL record cell name maximum to be the 256 allowed by
the protocol and ignore the record retrieved by YFSVL.GetCellName if it
exceeds 253.

Fixes: c3e9f888263b ("afs: Implement client support for the YFSVL.GetCellName RPC op")
Reported-by: syzbot+7848fee1f1e5c53f912b at syzkaller.appspotmail.com
Closes: https://lore.kernel.org/r/6776d25d.050a0220.3a8527.0048.GAE at google.com/
Signed-off-by: David Howells <dhowells at redhat.com>
Tested-by: syzbot+7848fee1f1e5c53f912b at syzkaller.appspotmail.com
cc: Marc Dionne <marc.dionne at auristor.com>
cc: linux-afs at lists.infradead.org
---
 fs/afs/afs.h      |    2 +-
 fs/afs/afs_vl.h   |    1 +
 fs/afs/vl_alias.c |    8 ++++++--
 fs/afs/vlclient.c |    2 +-
 4 files changed, 9 insertions(+), 4 deletions(-)

diff --git a/fs/afs/afs.h b/fs/afs/afs.h
index b488072aee87..ec3db00bd081 100644
--- a/fs/afs/afs.h
+++ b/fs/afs/afs.h
@@ -10,7 +10,7 @@
 
 #include <linux/in.h>
 
-#define AFS_MAXCELLNAME		256  	/* Maximum length of a cell name */
+#define AFS_MAXCELLNAME		253  	/* Maximum length of a cell name (DNS limited) */
 #define AFS_MAXVOLNAME		64  	/* Maximum length of a volume name */
 #define AFS_MAXNSERVERS		8   	/* Maximum servers in a basic volume record */
 #define AFS_NMAXNSERVERS	13  	/* Maximum servers in a N/U-class volume record */
diff --git a/fs/afs/afs_vl.h b/fs/afs/afs_vl.h
index a06296c8827d..b835e25a2c02 100644
--- a/fs/afs/afs_vl.h
+++ b/fs/afs/afs_vl.h
@@ -13,6 +13,7 @@
 #define AFS_VL_PORT		7003	/* volume location service port */
 #define VL_SERVICE		52	/* RxRPC service ID for the Volume Location service */
 #define YFS_VL_SERVICE		2503	/* Service ID for AuriStor upgraded VL service */
+#define YFS_VL_MAXCELLNAME	256  	/* Maximum length of a cell name in YFS protocol */
 
 enum AFSVL_Operations {
 	VLGETENTRYBYID		= 503,	/* AFS Get VLDB entry by ID */
diff --git a/fs/afs/vl_alias.c b/fs/afs/vl_alias.c
index 9f36e14f1c2d..f9e76b604f31 100644
--- a/fs/afs/vl_alias.c
+++ b/fs/afs/vl_alias.c
@@ -253,6 +253,7 @@ static char *afs_vl_get_cell_name(struct afs_cell *cell, struct key *key)
 static int yfs_check_canonical_cell_name(struct afs_cell *cell, struct key *key)
 {
 	struct afs_cell *master;
+	size_t name_len;
 	char *cell_name;
 
 	cell_name = afs_vl_get_cell_name(cell, key);
@@ -264,8 +265,11 @@ static int yfs_check_canonical_cell_name(struct afs_cell *cell, struct key *key)
 		return 0;
 	}
 
-	master = afs_lookup_cell(cell->net, cell_name, strlen(cell_name),
-				 NULL, false);
+	name_len = strlen(cell_name);
+	if (!name_len || name_len > AFS_MAXCELLNAME)
+		master = ERR_PTR(-EOPNOTSUPP);
+	else
+		master = afs_lookup_cell(cell->net, cell_name, name_len, NULL, false);
 	kfree(cell_name);
 	if (IS_ERR(master))
 		return PTR_ERR(master);
diff --git a/fs/afs/vlclient.c b/fs/afs/vlclient.c
index cac75f89b64a..55dd0fc5aad7 100644
--- a/fs/afs/vlclient.c
+++ b/fs/afs/vlclient.c
@@ -697,7 +697,7 @@ static int afs_deliver_yfsvl_get_cell_name(struct afs_call *call)
 			return ret;
 
 		namesz = ntohl(call->tmp);
-		if (namesz > AFS_MAXCELLNAME)
+		if (namesz > YFS_VL_MAXCELLNAME)
 			return afs_protocol_error(call, afs_eproto_cellname_len);
 		paddedsz = (namesz + 3) & ~3;
 		call->count = namesz;



From dhowells at redhat.com  Mon Jan  6 08:44:45 2025
From: dhowells at redhat.com (David Howells)
Date: Mon, 06 Jan 2025 16:44:45 +0000
Subject: [syzbot] [afs?] WARNING: lock held when returning to user space in afs_proc_addr_prefs_write
In-Reply-To: <676c7f0d.050a0220.2f3838.03bb.GAE@google.com>
References: <676c7f0d.050a0220.2f3838.03bb.GAE@google.com>
Message-ID: <377468.1736181885@warthog.procyon.org.uk>

#syz test: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git master

[Retrying due to previous boot failure]

afs: Merge preference rule failure condition

syzbot reported a lock held when returning to userspace[1].  This is
because if argc is less than 0 and the function returns directly, the held
inode lock is not released.

Fix this by store the error in ret and jump to done to clean up instead of
returning directly.

[dh: Modified Lizhi Xu's original patch to make it honour the error code
from afs_split_string()]

[1]
WARNING: lock held when returning to user space!
6.13.0-rc3-syzkaller-00209-g499551201b5f #0 Not tainted
------------------------------------------------
syz-executor133/5823 is leaving the kernel with locks still held!
1 lock held by syz-executor133/5823:
 #0: ffff888071cffc00 (&sb->s_type->i_mutex_key#9){++++}-{4:4}, at: inode_lock include/linux/fs.h:818 [inline]
 #0: ffff888071cffc00 (&sb->s_type->i_mutex_key#9){++++}-{4:4}, at: afs_proc_addr_prefs_write+0x2bb/0x14e0 fs/afs/addr_prefs.c:388

Reported-by: syzbot+76f33569875eb708e575 at syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=76f33569875eb708e575
Signed-off-by: Lizhi Xu <lizhi.xu at windriver.com>
Signed-off-by: David Howells <dhowells at redhat.com>
cc: Marc Dionne <marc.dionne at auristor.com>
cc: linux-afs at lists.infradead.org
Link: https://lore.kernel.org/r/20241226012616.2348907-1-lizhi.xu at windriver.com/
---
diff --git a/fs/afs/addr_prefs.c b/fs/afs/addr_prefs.c
index a189ff8a5034..c0384201b8fe 100644
--- a/fs/afs/addr_prefs.c
+++ b/fs/afs/addr_prefs.c
@@ -413,8 +413,10 @@ int afs_proc_addr_prefs_write(struct file *file, char *buf, size_t size)
 
 	do {
 		argc = afs_split_string(&buf, argv, ARRAY_SIZE(argv));
-		if (argc < 0)
-			return argc;
+		if (argc < 0) {
+			ret = argc;
+			goto done;
+		}
 		if (argc < 2)
 			goto inval;
 


From syzbot+76f33569875eb708e575 at syzkaller.appspotmail.com  Mon Jan  6 09:05:02 2025
From: syzbot+76f33569875eb708e575 at syzkaller.appspotmail.com (syzbot)
Date: Mon, 06 Jan 2025 09:05:02 -0800
Subject: [syzbot] [afs?] WARNING: lock held when returning to user space
 in afs_proc_addr_prefs_write
In-Reply-To: <377468.1736181885@warthog.procyon.org.uk>
Message-ID: <677c0d3e.050a0220.3b3668.0013.GAE@google.com>

Hello,

syzbot has tested the proposed patch and the reproducer did not trigger any issue:

Reported-by: syzbot+76f33569875eb708e575 at syzkaller.appspotmail.com
Tested-by: syzbot+76f33569875eb708e575 at syzkaller.appspotmail.com

Tested on:

commit:         13563da6 Merge tag 'vfio-v6.13-rc7' of https://github...
git tree:       upstream
console output: https://syzkaller.appspot.com/x/log.txt?x=154cb6f8580000
kernel config:  https://syzkaller.appspot.com/x/.config?x=ad08f7f48e13abcd
dashboard link: https://syzkaller.appspot.com/bug?extid=76f33569875eb708e575
compiler:       gcc (Debian 12.2.0-14) 12.2.0, GNU ld (GNU Binutils for Debian) 2.40
patch:          https://syzkaller.appspot.com/x/patch.diff?x=164dc4b0580000

Note: testing is done by a robot and is best-effort only.


From jaltman at auristor.com  Mon Jan  6 10:58:51 2025
From: jaltman at auristor.com (Jeffrey E Altman)
Date: Mon, 6 Jan 2025 13:58:51 -0500
Subject: [PATCH] afs: Fix the maximum cell name length
In-Reply-To: <376236.1736180460@warthog.procyon.org.uk>
References: <376236.1736180460@warthog.procyon.org.uk>
Message-ID: <893fd943-f830-4baf-bfdc-2b7e4a4bc99a@auristor.com>

On 1/6/2025 11:21 AM, David Howells wrote:
> The kafs filesystem limits the maximum length of a cell to 256 bytes, but a
> problem occurs if someone actually does that: kafs tries to create a
> directory under /proc/net/afs/ with the name of the cell, but that fails
> with a warning:
>
>          WARNING: CPU: 0 PID: 9 at fs/proc/generic.c:405
>
> because procfs limits the maximum filename length to 255.
>
> However, the DNS limits the maximum lookup length and, by extension, the
> maximum cell name, to 255 less two (length count and trailing NUL).
>
> Fix this by limiting the maximum acceptable cellname length to 253.  This
> also allows us to be sure we can create the "/afs/.<cell>/" mountpoint too.
>
> Further, split the YFS VL record cell name maximum to be the 256 allowed by
> the protocol and ignore the record retrieved by YFSVL.GetCellName if it
> exceeds 253.
>
> Fixes: c3e9f888263b ("afs: Implement client support for the YFSVL.GetCellName RPC op")
> Reported-by: syzbot+7848fee1f1e5c53f912b at syzkaller.appspotmail.com
> Closes: https://lore.kernel.org/r/6776d25d.050a0220.3a8527.0048.GAE at google.com/
> Signed-off-by: David Howells <dhowells at redhat.com>
> Tested-by: syzbot+7848fee1f1e5c53f912b at syzkaller.appspotmail.com
> cc: Marc Dionne <marc.dionne at auristor.com>
> cc: linux-afs at lists.infradead.org
> ---
>   fs/afs/afs.h      |    2 +-
>   fs/afs/afs_vl.h   |    1 +
>   fs/afs/vl_alias.c |    8 ++++++--
>   fs/afs/vlclient.c |    2 +-
>   4 files changed, 9 insertions(+), 4 deletions(-)
>
> diff --git a/fs/afs/afs.h b/fs/afs/afs.h
> index b488072aee87..ec3db00bd081 100644
> --- a/fs/afs/afs.h
> +++ b/fs/afs/afs.h
> @@ -10,7 +10,7 @@
>   
>   #include <linux/in.h>
>   
> -#define AFS_MAXCELLNAME		256  	/* Maximum length of a cell name */
> +#define AFS_MAXCELLNAME		253  	/* Maximum length of a cell name (DNS limited) */
>   #define AFS_MAXVOLNAME		64  	/* Maximum length of a volume name */
>   #define AFS_MAXNSERVERS		8   	/* Maximum servers in a basic volume record */
>   #define AFS_NMAXNSERVERS	13  	/* Maximum servers in a N/U-class volume record */
> diff --git a/fs/afs/afs_vl.h b/fs/afs/afs_vl.h
> index a06296c8827d..b835e25a2c02 100644
> --- a/fs/afs/afs_vl.h
> +++ b/fs/afs/afs_vl.h
> @@ -13,6 +13,7 @@
>   #define AFS_VL_PORT		7003	/* volume location service port */
>   #define VL_SERVICE		52	/* RxRPC service ID for the Volume Location service */
>   #define YFS_VL_SERVICE		2503	/* Service ID for AuriStor upgraded VL service */
> +#define YFS_VL_MAXCELLNAME	256  	/* Maximum length of a cell name in YFS protocol */
>   
>   enum AFSVL_Operations {
>   	VLGETENTRYBYID		= 503,	/* AFS Get VLDB entry by ID */
> diff --git a/fs/afs/vl_alias.c b/fs/afs/vl_alias.c
> index 9f36e14f1c2d..f9e76b604f31 100644
> --- a/fs/afs/vl_alias.c
> +++ b/fs/afs/vl_alias.c
> @@ -253,6 +253,7 @@ static char *afs_vl_get_cell_name(struct afs_cell *cell, struct key *key)
>   static int yfs_check_canonical_cell_name(struct afs_cell *cell, struct key *key)
>   {
>   	struct afs_cell *master;
> +	size_t name_len;
>   	char *cell_name;
>   
>   	cell_name = afs_vl_get_cell_name(cell, key);
> @@ -264,8 +265,11 @@ static int yfs_check_canonical_cell_name(struct afs_cell *cell, struct key *key)
>   		return 0;
>   	}
>   
> -	master = afs_lookup_cell(cell->net, cell_name, strlen(cell_name),
> -				 NULL, false);
> +	name_len = strlen(cell_name);
> +	if (!name_len || name_len > AFS_MAXCELLNAME)
> +		master = ERR_PTR(-EOPNOTSUPP);
> +	else
> +		master = afs_lookup_cell(cell->net, cell_name, name_len, NULL, false);
>   	kfree(cell_name);
>   	if (IS_ERR(master))
>   		return PTR_ERR(master);
> diff --git a/fs/afs/vlclient.c b/fs/afs/vlclient.c
> index cac75f89b64a..55dd0fc5aad7 100644
> --- a/fs/afs/vlclient.c
> +++ b/fs/afs/vlclient.c
> @@ -697,7 +697,7 @@ static int afs_deliver_yfsvl_get_cell_name(struct afs_call *call)
>   			return ret;
>   
>   		namesz = ntohl(call->tmp);
> -		if (namesz > AFS_MAXCELLNAME)
> +		if (namesz > YFS_VL_MAXCELLNAME)
>   			return afs_protocol_error(call, afs_eproto_cellname_len);
>   		paddedsz = (namesz + 3) & ~3;
>   		call->count = namesz;
>
>
> _______________________________________________
> linux-afs mailing list
> http://lists.infradead.org/mailman/listinfo/linux-afs

Reviewed-by: Jeffrey Altman <jaltman at auristor.com>


-------------- next part --------------
A non-text attachment was scrubbed...
Name: smime.p7s
Type: application/pkcs7-signature
Size: 4276 bytes
Desc: S/MIME Cryptographic Signature
URL: <http://lists.infradead.org/pipermail/linux-afs/attachments/20250106/437d78ac/attachment.p7s>

From dhowells at redhat.com  Tue Jan  7 06:25:08 2025
From: dhowells at redhat.com (David Howells)
Date: Tue,  7 Jan 2025 14:25:08 +0000
Subject: [PATCH 1/2] afs: Make /afs/.<cell> as well /afs/<cell> mountpoints
In-Reply-To: <20250107142513.527300-1-dhowells@redhat.com>
References: <20250107142513.527300-1-dhowells@redhat.com>
Message-ID: <20250107142513.527300-2-dhowells@redhat.com>

When a cell is instantiated, automatically create an /afs/.<cell>
mountpoint to match the /afs/<cell> mountpoint to match other AFS clients.

Signed-off-by: David Howells <dhowells at redhat.com>
cc: Marc Dionne <marc.dionne at auristor.com>
cc: linux-afs at lists.infradead.org
---
 fs/afs/cell.c    | 13 +++++++-----
 fs/afs/dynroot.c | 52 ++++++++++++++++++++++++++++++------------------
 2 files changed, 41 insertions(+), 24 deletions(-)

diff --git a/fs/afs/cell.c b/fs/afs/cell.c
index caa09875f520..1aba6d4d03a9 100644
--- a/fs/afs/cell.c
+++ b/fs/afs/cell.c
@@ -146,18 +146,20 @@ static struct afs_cell *afs_alloc_cell(struct afs_net *net,
 		return ERR_PTR(-ENOMEM);
 	}
 
-	cell->name = kmalloc(namelen + 1, GFP_KERNEL);
+	cell->name = kmalloc(1 + namelen + 1, GFP_KERNEL);
 	if (!cell->name) {
 		kfree(cell);
 		return ERR_PTR(-ENOMEM);
 	}
 
-	cell->net = net;
+	cell->name[0] = '.';
+	cell->name++;
 	cell->name_len = namelen;
 	for (i = 0; i < namelen; i++)
 		cell->name[i] = tolower(name[i]);
 	cell->name[i] = 0;
 
+	cell->net = net;
 	refcount_set(&cell->ref, 1);
 	atomic_set(&cell->active, 0);
 	INIT_WORK(&cell->manager, afs_manage_cell_work);
@@ -211,7 +213,7 @@ static struct afs_cell *afs_alloc_cell(struct afs_net *net,
 	if (ret == -EINVAL)
 		printk(KERN_ERR "kAFS: bad VL server IP address\n");
 error:
-	kfree(cell->name);
+	kfree(cell->name - 1);
 	kfree(cell);
 	_leave(" = %d", ret);
 	return ERR_PTR(ret);
@@ -502,7 +504,7 @@ static void afs_cell_destroy(struct rcu_head *rcu)
 	afs_put_vlserverlist(net, rcu_access_pointer(cell->vl_servers));
 	afs_unuse_cell(net, cell->alias_of, afs_cell_trace_unuse_alias);
 	key_put(cell->anonymous_key);
-	kfree(cell->name);
+	kfree(cell->name - 1);
 	kfree(cell);
 
 	afs_dec_cells_outstanding(net);
@@ -710,7 +712,8 @@ static void afs_deactivate_cell(struct afs_net *net, struct afs_cell *cell)
 	afs_proc_cell_remove(cell);
 
 	mutex_lock(&net->proc_cells_lock);
-	hlist_del_rcu(&cell->proc_link);
+	if (!hlist_unhashed(&cell->proc_link))
+		hlist_del_rcu(&cell->proc_link);
 	afs_dynroot_rmdir(net, cell);
 	mutex_unlock(&net->proc_cells_lock);
 
diff --git a/fs/afs/dynroot.c b/fs/afs/dynroot.c
index c4d2711e20ad..f80a4244b9d2 100644
--- a/fs/afs/dynroot.c
+++ b/fs/afs/dynroot.c
@@ -271,7 +271,8 @@ const struct dentry_operations afs_dynroot_dentry_operations = {
 int afs_dynroot_mkdir(struct afs_net *net, struct afs_cell *cell)
 {
 	struct super_block *sb = net->dynroot_sb;
-	struct dentry *root, *subdir;
+	struct dentry *root, *subdir, *dsubdir;
+	char *dotname = cell->name - 1;
 	int ret;
 
 	if (!sb || atomic_read(&sb->s_active) == 0)
@@ -286,34 +287,31 @@ int afs_dynroot_mkdir(struct afs_net *net, struct afs_cell *cell)
 		goto unlock;
 	}
 
-	/* Note that we're retaining an extra ref on the dentry */
+	dsubdir = lookup_one_len(dotname, root, cell->name_len + 1);
+	if (IS_ERR(dsubdir)) {
+		ret = PTR_ERR(dsubdir);
+		dput(subdir);
+		goto unlock;
+	}
+
+	/* Note that we're retaining extra refs on the dentries. */
 	subdir->d_fsdata = (void *)1UL;
+	dsubdir->d_fsdata = (void *)1UL;
 	ret = 0;
 unlock:
 	inode_unlock(root->d_inode);
 	return ret;
 }
 
-/*
- * Remove a manually added cell mount directory.
- * - The caller must hold net->proc_cells_lock
- */
-void afs_dynroot_rmdir(struct afs_net *net, struct afs_cell *cell)
+static void afs_dynroot_rm_one_dir(struct dentry *root, const char *name, size_t name_len)
 {
-	struct super_block *sb = net->dynroot_sb;
-	struct dentry *root, *subdir;
-
-	if (!sb || atomic_read(&sb->s_active) == 0)
-		return;
-
-	root = sb->s_root;
-	inode_lock(root->d_inode);
+	struct dentry *subdir;
 
 	/* Don't want to trigger a lookup call, which will re-add the cell */
-	subdir = try_lookup_one_len(cell->name, root, cell->name_len);
+	subdir = try_lookup_one_len(name, root, name_len);
 	if (IS_ERR_OR_NULL(subdir)) {
 		_debug("lookup %ld", PTR_ERR(subdir));
-		goto no_dentry;
+		return;
 	}
 
 	_debug("rmdir %pd %u", subdir, d_count(subdir));
@@ -324,8 +322,24 @@ void afs_dynroot_rmdir(struct afs_net *net, struct afs_cell *cell)
 		dput(subdir);
 	}
 	dput(subdir);
-no_dentry:
-	inode_unlock(root->d_inode);
+}
+
+/*
+ * Remove a manually added cell mount directory.
+ * - The caller must hold net->proc_cells_lock
+ */
+void afs_dynroot_rmdir(struct afs_net *net, struct afs_cell *cell)
+{
+	struct super_block *sb = net->dynroot_sb;
+	char *dotname = cell->name - 1;
+
+	if (!sb || atomic_read(&sb->s_active) == 0)
+		return;
+
+	inode_lock(sb->s_root->d_inode);
+	afs_dynroot_rm_one_dir(sb->s_root, cell->name, cell->name_len);
+	afs_dynroot_rm_one_dir(sb->s_root, dotname, cell->name_len + 1);
+	inode_unlock(sb->s_root->d_inode);
 	_leave("");
 }
 



From dhowells at redhat.com  Tue Jan  7 06:25:07 2025
From: dhowells at redhat.com (David Howells)
Date: Tue,  7 Jan 2025 14:25:07 +0000
Subject: [PATCH 0/2] afs: Dynamic root improvements
Message-ID: <20250107142513.527300-1-dhowells@redhat.com>

Here's a pair of patches to make a number of improvements to the AFS
dynamic root:

 (1) Create an /afs/.<cell> mountpoint to match the /afs/<cell> mountpoint
     when a cell is created.

 (2) Change the handling of /afs/@cell from being a dentry name
     substitution at lookup time to making it a symlink to the current cell
     name and also provide a /afs/. at cell symlink to point to the dotted
     cell mountpoint.

The patches are here:

	http://git.kernel.org/cgit/linux/kernel/git/dhowells/linux-fs.git/log/?h=afs-next

Thanks,
David

David Howells (2):
  afs: Make /afs/.<cell> as well /afs/<cell> mountpoints
  afs: Make /afs/@cell and /afs/. at cell symlinks

 fs/afs/cell.c              |  13 ++-
 fs/afs/dynroot.c           | 190 +++++++++++++++++++++++++++----------
 include/trace/events/afs.h |   2 +
 3 files changed, 152 insertions(+), 53 deletions(-)



From dhowells at redhat.com  Tue Jan  7 06:25:09 2025
From: dhowells at redhat.com (David Howells)
Date: Tue,  7 Jan 2025 14:25:09 +0000
Subject: [PATCH 2/2] afs: Make /afs/@cell and /afs/.@cell symlinks
In-Reply-To: <20250107142513.527300-1-dhowells@redhat.com>
References: <20250107142513.527300-1-dhowells@redhat.com>
Message-ID: <20250107142513.527300-3-dhowells@redhat.com>

Make /afs/@cell a symlink in the /afs dynamic root to match what other AFS
clients do rather than doing a substitution in the dentry name.  This has
the bonus of being tab-expandable also.

Further, provide a /afs/. at cell symlink to point to the dotted cell share.

Signed-off-by: David Howells <dhowells at redhat.com>
cc: Marc Dionne <marc.dionne at auristor.com>
cc: linux-afs at lists.infradead.org
---
 fs/afs/dynroot.c           | 140 +++++++++++++++++++++++++++++--------
 include/trace/events/afs.h |   2 +
 2 files changed, 112 insertions(+), 30 deletions(-)

diff --git a/fs/afs/dynroot.c b/fs/afs/dynroot.c
index f80a4244b9d2..5a53631239f1 100644
--- a/fs/afs/dynroot.c
+++ b/fs/afs/dynroot.c
@@ -11,6 +11,8 @@
 #include "internal.h"
 
 static atomic_t afs_autocell_ino;
+static const char afs_atcell[] = "@cell";
+static const char afs_dotatcell[] = ". at cell";
 
 /*
  * iget5() comparator for inode created by autocell operations
@@ -185,48 +187,84 @@ struct inode *afs_try_auto_mntpt(struct dentry *dentry, struct inode *dir)
 	return ret == -ENOENT ? NULL : ERR_PTR(ret);
 }
 
+static void afs_atcell_delayed_put_cell(void *arg)
+{
+	struct afs_cell *cell = arg;
+
+	afs_put_cell(cell, afs_cell_trace_put_atcell);
+}
+
 /*
- * Look up @cell in a dynroot directory.  This is a substitution for the
- * local cell name for the net namespace.
+ * Read @cell or . at cell symlinks.
  */
-static struct dentry *afs_lookup_atcell(struct dentry *dentry)
+static const char *afs_atcell_get_link(struct dentry *dentry, struct inode *inode,
+				       struct delayed_call *done)
 {
+	struct afs_vnode *vnode = AFS_FS_I(inode);
 	struct afs_cell *cell;
-	struct afs_net *net = afs_d2net(dentry);
-	struct dentry *ret;
-	char *name;
-	int len;
+	struct afs_net *net = afs_i2net(inode);
+	const char *name;
+	bool dotted = vnode->fid.vnode == 3;
 
 	if (!net->ws_cell)
 		return ERR_PTR(-ENOENT);
 
-	ret = ERR_PTR(-ENOMEM);
-	name = kmalloc(AFS_MAXCELLNAME + 1, GFP_KERNEL);
-	if (!name)
-		goto out_p;
-
 	down_read(&net->cells_lock);
+
 	cell = net->ws_cell;
-	if (cell) {
-		len = cell->name_len;
-		memcpy(name, cell->name, len + 1);
-	}
+	if (dotted)
+		name = cell->name - 1;
+	else
+		name = cell->name;
+	afs_get_cell(cell, afs_cell_trace_get_atcell);
+	set_delayed_call(done, afs_atcell_delayed_put_cell, cell);
+
 	up_read(&net->cells_lock);
+	return name;
+}
 
-	ret = ERR_PTR(-ENOENT);
-	if (!cell)
-		goto out_n;
+static const struct inode_operations afs_atcell_inode_operations = {
+	.get_link	= afs_atcell_get_link,
+};
 
-	ret = lookup_one_len(name, dentry->d_parent, len);
+/*
+ * Look up @cell or . at cell in a dynroot directory.  This is a substitution for
+ * the local cell name for the net namespace.
+ */
+static struct dentry *afs_lookup_atcell(struct dentry *dentry, bool dotted)
+{
+	struct afs_vnode *vnode;
+	struct afs_fid fid = { .vnode = 2, .unique = 1, };
+	struct inode *inode;
 
-	/* We don't want to d_add() the @cell dentry here as we don't want to
-	 * the cached dentry to hide changes to the local cell name.
-	 */
+	if (dotted)
+		fid.vnode = 3;
 
-out_n:
-	kfree(name);
-out_p:
-	return ret;
+
+	inode = iget5_locked(dentry->d_sb, fid.vnode,
+			     afs_iget5_pseudo_test, afs_iget5_pseudo_set, &fid);
+	if (!inode)
+		return ERR_PTR(-ENOMEM);
+
+	vnode = AFS_FS_I(inode);
+
+	/* there shouldn't be an existing inode */
+	BUG_ON(!(inode->i_state & I_NEW));
+
+	netfs_inode_init(&vnode->netfs, NULL, false);
+	simple_inode_init_ts(inode);
+	set_nlink(inode, 1);
+	inode->i_size		= 0;
+	inode->i_mode		= S_IFLNK | S_IRUGO | S_IXUGO;
+	inode->i_op		= &afs_atcell_inode_operations;
+	inode->i_uid		= GLOBAL_ROOT_UID;
+	inode->i_gid		= GLOBAL_ROOT_GID;
+	inode->i_blocks		= 0;
+	inode->i_generation	= 0;
+	inode->i_flags		|= S_NOATIME;
+
+	unlock_new_inode(inode);
+	return d_splice_alias(inode, dentry);
 }
 
 /*
@@ -247,9 +285,13 @@ static struct dentry *afs_dynroot_lookup(struct inode *dir, struct dentry *dentr
 		return ERR_PTR(-ENAMETOOLONG);
 	}
 
-	if (dentry->d_name.len == 5 &&
-	    memcmp(dentry->d_name.name, "@cell", 5) == 0)
-		return afs_lookup_atcell(dentry);
+	if (dentry->d_name.len == sizeof(afs_atcell) - 1 &&
+	    memcmp(dentry->d_name.name, afs_atcell, sizeof(afs_atcell) - 1) == 0)
+		return afs_lookup_atcell(dentry, false);
+
+	if (dentry->d_name.len == sizeof(afs_dotatcell) - 1 &&
+	    memcmp(dentry->d_name.name, afs_dotatcell, sizeof(afs_dotatcell) - 1) == 0)
+		return afs_lookup_atcell(dentry, true);
 
 	return d_splice_alias(afs_try_auto_mntpt(dentry, dir), dentry);
 }
@@ -343,6 +385,40 @@ void afs_dynroot_rmdir(struct afs_net *net, struct afs_cell *cell)
 	_leave("");
 }
 
+/*
+ * Create @cell and . at cell symlinks.
+ */
+static int afs_dynroot_symlink(struct afs_net *net)
+{
+	struct super_block *sb = net->dynroot_sb;
+	struct dentry *root, *symlink, *dsymlink;
+	int ret;
+
+	/* Let the ->lookup op do the creation */
+	root = sb->s_root;
+	inode_lock(root->d_inode);
+	symlink = lookup_one_len(afs_atcell, root, sizeof(afs_atcell) - 1);
+	if (IS_ERR(symlink)) {
+		ret = PTR_ERR(symlink);
+		goto unlock;
+	}
+
+	dsymlink = lookup_one_len(afs_dotatcell, root, sizeof(afs_dotatcell) - 1);
+	if (IS_ERR(dsymlink)) {
+		ret = PTR_ERR(dsymlink);
+		dput(symlink);
+		goto unlock;
+	}
+
+	/* Note that we're retaining extra refs on the dentries. */
+	symlink->d_fsdata = (void *)1UL;
+	dsymlink->d_fsdata = (void *)1UL;
+	ret = 0;
+unlock:
+	inode_unlock(root->d_inode);
+	return ret;
+}
+
 /*
  * Populate a newly created dynamic root with cell names.
  */
@@ -355,6 +431,10 @@ int afs_dynroot_populate(struct super_block *sb)
 	mutex_lock(&net->proc_cells_lock);
 
 	net->dynroot_sb = sb;
+	ret = afs_dynroot_symlink(net);
+	if (ret < 0)
+		goto error;
+
 	hlist_for_each_entry(cell, &net->proc_cells, proc_link) {
 		ret = afs_dynroot_mkdir(net, cell);
 		if (ret < 0)
diff --git a/include/trace/events/afs.h b/include/trace/events/afs.h
index a0aed1a428a1..de0e2301a037 100644
--- a/include/trace/events/afs.h
+++ b/include/trace/events/afs.h
@@ -168,12 +168,14 @@ enum yfs_cm_operation {
 #define afs_cell_traces \
 	EM(afs_cell_trace_alloc,		"ALLOC     ") \
 	EM(afs_cell_trace_free,			"FREE      ") \
+	EM(afs_cell_trace_get_atcell,		"GET atcell") \
 	EM(afs_cell_trace_get_queue_dns,	"GET q-dns ") \
 	EM(afs_cell_trace_get_queue_manage,	"GET q-mng ") \
 	EM(afs_cell_trace_get_queue_new,	"GET q-new ") \
 	EM(afs_cell_trace_get_vol,		"GET vol   ") \
 	EM(afs_cell_trace_insert,		"INSERT    ") \
 	EM(afs_cell_trace_manage,		"MANAGE    ") \
+	EM(afs_cell_trace_put_atcell,		"PUT atcell") \
 	EM(afs_cell_trace_put_candidate,	"PUT candid") \
 	EM(afs_cell_trace_put_destroy,		"PUT destry") \
 	EM(afs_cell_trace_put_queue_work,	"PUT q-work") \



From dhowells at redhat.com  Tue Jan  7 06:52:32 2025
From: dhowells at redhat.com (David Howells)
Date: Tue, 07 Jan 2025 14:52:32 +0000
Subject: [PATCH] afs: Fix merge preference rule failure condition
Message-ID: <529850.1736261552@warthog.procyon.org.uk>

syzbot reported a lock held when returning to userspace[1].  This is
because if argc is less than 0 and the function returns directly, the held
inode lock is not released.

Fix this by store the error in ret and jump to done to clean up instead of
returning directly.

[dh: Modified Lizhi Xu's original patch to make it honour the error code
from afs_split_string()]

[1]
WARNING: lock held when returning to user space!
6.13.0-rc3-syzkaller-00209-g499551201b5f #0 Not tainted
------------------------------------------------
syz-executor133/5823 is leaving the kernel with locks still held!
1 lock held by syz-executor133/5823:
 #0: ffff888071cffc00 (&sb->s_type->i_mutex_key#9){++++}-{4:4}, at: inode_lock include/linux/fs.h:818 [inline]
 #0: ffff888071cffc00 (&sb->s_type->i_mutex_key#9){++++}-{4:4}, at: afs_proc_addr_prefs_write+0x2bb/0x14e0 fs/afs/addr_prefs.c:388

Reported-by: syzbot+76f33569875eb708e575 at syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=76f33569875eb708e575
Signed-off-by: Lizhi Xu <lizhi.xu at windriver.com>
Signed-off-by: David Howells <dhowells at redhat.com>
Tested-by: syzbot+76f33569875eb708e575 at syzkaller.appspotmail.com
cc: Marc Dionne <marc.dionne at auristor.com>
cc: linux-afs at lists.infradead.org
Link: https://lore.kernel.org/r/20241226012616.2348907-1-lizhi.xu at windriver.com/
---
diff --git a/fs/afs/addr_prefs.c b/fs/afs/addr_prefs.c
index a189ff8a5034..c0384201b8fe 100644
--- a/fs/afs/addr_prefs.c
+++ b/fs/afs/addr_prefs.c
@@ -413,8 +413,10 @@ int afs_proc_addr_prefs_write(struct file *file, char *buf, size_t size)
 
 	do {
 		argc = afs_split_string(&buf, argv, ARRAY_SIZE(argv));
-		if (argc < 0)
-			return argc;
+		if (argc < 0) {
+			ret = argc;
+			goto done;
+		}
 		if (argc < 2)
 			goto inval;
 


From brauner at kernel.org  Tue Jan  7 06:55:44 2025
From: brauner at kernel.org (Christian Brauner)
Date: Tue,  7 Jan 2025 15:55:44 +0100
Subject: [PATCH] afs: Fix the maximum cell name length
In-Reply-To: <376236.1736180460@warthog.procyon.org.uk>
References: <376236.1736180460@warthog.procyon.org.uk>
Message-ID: <20250107-fundgrube-unpassend-106795b45fcf@brauner>

On Mon, 06 Jan 2025 16:21:00 +0000, David Howells wrote:
> The kafs filesystem limits the maximum length of a cell to 256 bytes, but a
> problem occurs if someone actually does that: kafs tries to create a
> directory under /proc/net/afs/ with the name of the cell, but that fails
> with a warning:
> 
>         WARNING: CPU: 0 PID: 9 at fs/proc/generic.c:405
> 
> [...]

Applied to the vfs.fixes branch of the vfs/vfs.git tree.
Patches in the vfs.fixes branch should appear in linux-next soon.

Please report any outstanding bugs that were missed during review in a
new review to the original patch series allowing us to drop it.

It's encouraged to provide Acked-bys and Reviewed-bys even though the
patch has now been applied. If possible patch trailers will be updated.

Note that commit hashes shown below are subject to change due to rebase,
trailer updates or similar. If in doubt, please check the listed branch.

tree:   https://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs.git
branch: vfs.fixes

[1/1] afs: Fix the maximum cell name length
      https://git.kernel.org/vfs/vfs/c/8fd56ad6e7c9


From viro at zeniv.linux.org.uk  Tue Jan  7 07:20:50 2025
From: viro at zeniv.linux.org.uk (Al Viro)
Date: Tue, 7 Jan 2025 15:20:50 +0000
Subject: [PATCH 2/2] afs: Make /afs/@cell and /afs/.@cell symlinks
In-Reply-To: <20250107142513.527300-3-dhowells@redhat.com>
References: <20250107142513.527300-1-dhowells@redhat.com>
 <20250107142513.527300-3-dhowells@redhat.com>
Message-ID: <20250107152050.GP1977892@ZenIV>

On Tue, Jan 07, 2025 at 02:25:09PM +0000, David Howells wrote:

> @@ -247,9 +285,13 @@ static struct dentry *afs_dynroot_lookup(struct inode *dir, struct dentry *dentr
>  		return ERR_PTR(-ENAMETOOLONG);
>  	}
>  
> -	if (dentry->d_name.len == 5 &&
> -	    memcmp(dentry->d_name.name, "@cell", 5) == 0)
> -		return afs_lookup_atcell(dentry);
> +	if (dentry->d_name.len == sizeof(afs_atcell) - 1 &&
> +	    memcmp(dentry->d_name.name, afs_atcell, sizeof(afs_atcell) - 1) == 0)
> +		return afs_lookup_atcell(dentry, false);
> +
> +	if (dentry->d_name.len == sizeof(afs_dotatcell) - 1 &&
> +	    memcmp(dentry->d_name.name, afs_dotatcell, sizeof(afs_dotatcell) - 1) == 0)
> +		return afs_lookup_atcell(dentry, true);

Ow...  That looks just painful.

>  	return d_splice_alias(afs_try_auto_mntpt(dentry, dir), dentry);
>  }
> @@ -343,6 +385,40 @@ void afs_dynroot_rmdir(struct afs_net *net, struct afs_cell *cell)

> +static int afs_dynroot_symlink(struct afs_net *net)
> +{
> +	struct super_block *sb = net->dynroot_sb;
> +	struct dentry *root, *symlink, *dsymlink;
> +	int ret;
> +
> +	/* Let the ->lookup op do the creation */
> +	root = sb->s_root;
> +	inode_lock(root->d_inode);
> +	symlink = lookup_one_len(afs_atcell, root, sizeof(afs_atcell) - 1);
> +	if (IS_ERR(symlink)) {
> +		ret = PTR_ERR(symlink);
> +		goto unlock;
> +	}
> +
> +	dsymlink = lookup_one_len(afs_dotatcell, root, sizeof(afs_dotatcell) - 1);
> +	if (IS_ERR(dsymlink)) {
> +		ret = PTR_ERR(dsymlink);
> +		dput(symlink);
> +		goto unlock;
> +	}

Just allocate those child dentries and call your afs_lookup_atcell() for them.
No need to keep that mess in ->lookup() - you are keeping those suckers cached
now, so...


From dhowells at redhat.com  Tue Jan  7 07:36:57 2025
From: dhowells at redhat.com (David Howells)
Date: Tue, 07 Jan 2025 15:36:57 +0000
Subject: [PATCH 2/2] afs: Make /afs/@cell and /afs/.@cell symlinks
In-Reply-To: <20250107152050.GP1977892@ZenIV>
References: <20250107152050.GP1977892@ZenIV> <20250107142513.527300-1-dhowells@redhat.com> <20250107142513.527300-3-dhowells@redhat.com>
Message-ID: <576579.1736264217@warthog.procyon.org.uk>

Al Viro <viro at zeniv.linux.org.uk> wrote:

> > +	dsymlink = lookup_one_len(afs_dotatcell, root, sizeof(afs_dotatcell) - 1);
> > +	if (IS_ERR(dsymlink)) {
> > +		ret = PTR_ERR(dsymlink);
> > +		dput(symlink);
> > +		goto unlock;
> > +	}
> 
> Just allocate those child dentries and call your afs_lookup_atcell() for them.
> No need to keep that mess in ->lookup() - you are keeping those suckers cached
> now, so...

Good point.  I need to do that for the cell mountpoints because someone can
arbitrarily create one by triggering a lookup of /afs/<cell>/, but for this
pair of symlinks, they're created before the sb goes live.

David



From dhowells at redhat.com  Tue Jan  7 09:56:48 2025
From: dhowells at redhat.com (David Howells)
Date: Tue, 07 Jan 2025 17:56:48 +0000
Subject: [PATCH 2/2] afs: Make /afs/@cell and /afs/.@cell symlinks
In-Reply-To: <20250107152050.GP1977892@ZenIV>
References: <20250107152050.GP1977892@ZenIV> <20250107142513.527300-1-dhowells@redhat.com> <20250107142513.527300-3-dhowells@redhat.com>
Message-ID: <599881.1736272608@warthog.procyon.org.uk>

Al Viro <viro at zeniv.linux.org.uk> wrote:

> Just allocate those child dentries and call your afs_lookup_atcell() for them.
> No need to keep that mess in ->lookup() - you are keeping those suckers cached
> now, so...

Actually, I wonder if creating the inodes and dentries for cells and the @cell
symlinks at mount of the dynamic root or when the cells are created is
actually the best way.

It might be better to list all the cells and symlinks in readdir and only
create them on demand in ->lookup().  The cells are kept in their own list on
the network namespace anyway, even if the dynamic root isn't mounted.

David



From dhowells at redhat.com  Tue Jan  7 10:34:48 2025
From: dhowells at redhat.com (David Howells)
Date: Tue,  7 Jan 2025 18:34:48 +0000
Subject: [PATCH v2 0/3] afs: Dynamic root improvements
Message-ID: <20250107183454.608451-1-dhowells@redhat.com>

Here are some patches to make a number of improvements to the AFS dynamic
root:

 (1) Create an /afs/.<cell> mountpoint to match the /afs/<cell> mountpoint
     when a cell is created.

 (2) Add some more checks on cell names proposed by the user to prevent
     dodgy symlink bodies from being created.  Also prevent rootcell from
     being altered once set to simplify the locking.

 (3) Change the handling of /afs/@cell from being a dentry name
     substitution at lookup time to making it a symlink to the current cell
     name and also provide a /afs/. at cell symlink to point to the dotted
     cell mountpoint.

The patches are here:

	http://git.kernel.org/cgit/linux/kernel/git/dhowells/linux-fs.git/log/?h=afs-next

Thanks,
David

Changes
=======
ver #2)
 - Add more checks on the cell names.
 - Prevent the rootcell from being altered once set.
 - Directly create the @cell symlinks rather than going through lookup.

Link: https://lore.kernel.org/r/20250107142513.527300-1-dhowells at redhat.com/ # v1

David Howells (3):
  afs: Make /afs/.<cell> as well as /afs/<cell> mountpoints
  afs: Add rootcell checks
  afs: Make /afs/@cell and /afs/. at cell symlinks

 fs/afs/cell.c              |  21 +++-
 fs/afs/dynroot.c           | 227 ++++++++++++++++++++++++++-----------
 fs/afs/proc.c              |   8 +-
 include/trace/events/afs.h |   2 +
 4 files changed, 186 insertions(+), 72 deletions(-)



From dhowells at redhat.com  Tue Jan  7 10:34:49 2025
From: dhowells at redhat.com (David Howells)
Date: Tue,  7 Jan 2025 18:34:49 +0000
Subject: [PATCH v2 1/3] afs: Make /afs/.<cell> as well as /afs/<cell> mountpoints
In-Reply-To: <20250107183454.608451-1-dhowells@redhat.com>
References: <20250107183454.608451-1-dhowells@redhat.com>
Message-ID: <20250107183454.608451-2-dhowells@redhat.com>

When a cell is instantiated, automatically create an /afs/.<cell>
mountpoint to match the /afs/<cell> mountpoint to match other AFS clients.

Signed-off-by: David Howells <dhowells at redhat.com>
cc: Marc Dionne <marc.dionne at auristor.com>
cc: linux-afs at lists.infradead.org
---
 fs/afs/cell.c    | 13 +++++++-----
 fs/afs/dynroot.c | 52 ++++++++++++++++++++++++++++++------------------
 2 files changed, 41 insertions(+), 24 deletions(-)

diff --git a/fs/afs/cell.c b/fs/afs/cell.c
index caa09875f520..1aba6d4d03a9 100644
--- a/fs/afs/cell.c
+++ b/fs/afs/cell.c
@@ -146,18 +146,20 @@ static struct afs_cell *afs_alloc_cell(struct afs_net *net,
 		return ERR_PTR(-ENOMEM);
 	}
 
-	cell->name = kmalloc(namelen + 1, GFP_KERNEL);
+	cell->name = kmalloc(1 + namelen + 1, GFP_KERNEL);
 	if (!cell->name) {
 		kfree(cell);
 		return ERR_PTR(-ENOMEM);
 	}
 
-	cell->net = net;
+	cell->name[0] = '.';
+	cell->name++;
 	cell->name_len = namelen;
 	for (i = 0; i < namelen; i++)
 		cell->name[i] = tolower(name[i]);
 	cell->name[i] = 0;
 
+	cell->net = net;
 	refcount_set(&cell->ref, 1);
 	atomic_set(&cell->active, 0);
 	INIT_WORK(&cell->manager, afs_manage_cell_work);
@@ -211,7 +213,7 @@ static struct afs_cell *afs_alloc_cell(struct afs_net *net,
 	if (ret == -EINVAL)
 		printk(KERN_ERR "kAFS: bad VL server IP address\n");
 error:
-	kfree(cell->name);
+	kfree(cell->name - 1);
 	kfree(cell);
 	_leave(" = %d", ret);
 	return ERR_PTR(ret);
@@ -502,7 +504,7 @@ static void afs_cell_destroy(struct rcu_head *rcu)
 	afs_put_vlserverlist(net, rcu_access_pointer(cell->vl_servers));
 	afs_unuse_cell(net, cell->alias_of, afs_cell_trace_unuse_alias);
 	key_put(cell->anonymous_key);
-	kfree(cell->name);
+	kfree(cell->name - 1);
 	kfree(cell);
 
 	afs_dec_cells_outstanding(net);
@@ -710,7 +712,8 @@ static void afs_deactivate_cell(struct afs_net *net, struct afs_cell *cell)
 	afs_proc_cell_remove(cell);
 
 	mutex_lock(&net->proc_cells_lock);
-	hlist_del_rcu(&cell->proc_link);
+	if (!hlist_unhashed(&cell->proc_link))
+		hlist_del_rcu(&cell->proc_link);
 	afs_dynroot_rmdir(net, cell);
 	mutex_unlock(&net->proc_cells_lock);
 
diff --git a/fs/afs/dynroot.c b/fs/afs/dynroot.c
index c4d2711e20ad..f80a4244b9d2 100644
--- a/fs/afs/dynroot.c
+++ b/fs/afs/dynroot.c
@@ -271,7 +271,8 @@ const struct dentry_operations afs_dynroot_dentry_operations = {
 int afs_dynroot_mkdir(struct afs_net *net, struct afs_cell *cell)
 {
 	struct super_block *sb = net->dynroot_sb;
-	struct dentry *root, *subdir;
+	struct dentry *root, *subdir, *dsubdir;
+	char *dotname = cell->name - 1;
 	int ret;
 
 	if (!sb || atomic_read(&sb->s_active) == 0)
@@ -286,34 +287,31 @@ int afs_dynroot_mkdir(struct afs_net *net, struct afs_cell *cell)
 		goto unlock;
 	}
 
-	/* Note that we're retaining an extra ref on the dentry */
+	dsubdir = lookup_one_len(dotname, root, cell->name_len + 1);
+	if (IS_ERR(dsubdir)) {
+		ret = PTR_ERR(dsubdir);
+		dput(subdir);
+		goto unlock;
+	}
+
+	/* Note that we're retaining extra refs on the dentries. */
 	subdir->d_fsdata = (void *)1UL;
+	dsubdir->d_fsdata = (void *)1UL;
 	ret = 0;
 unlock:
 	inode_unlock(root->d_inode);
 	return ret;
 }
 
-/*
- * Remove a manually added cell mount directory.
- * - The caller must hold net->proc_cells_lock
- */
-void afs_dynroot_rmdir(struct afs_net *net, struct afs_cell *cell)
+static void afs_dynroot_rm_one_dir(struct dentry *root, const char *name, size_t name_len)
 {
-	struct super_block *sb = net->dynroot_sb;
-	struct dentry *root, *subdir;
-
-	if (!sb || atomic_read(&sb->s_active) == 0)
-		return;
-
-	root = sb->s_root;
-	inode_lock(root->d_inode);
+	struct dentry *subdir;
 
 	/* Don't want to trigger a lookup call, which will re-add the cell */
-	subdir = try_lookup_one_len(cell->name, root, cell->name_len);
+	subdir = try_lookup_one_len(name, root, name_len);
 	if (IS_ERR_OR_NULL(subdir)) {
 		_debug("lookup %ld", PTR_ERR(subdir));
-		goto no_dentry;
+		return;
 	}
 
 	_debug("rmdir %pd %u", subdir, d_count(subdir));
@@ -324,8 +322,24 @@ void afs_dynroot_rmdir(struct afs_net *net, struct afs_cell *cell)
 		dput(subdir);
 	}
 	dput(subdir);
-no_dentry:
-	inode_unlock(root->d_inode);
+}
+
+/*
+ * Remove a manually added cell mount directory.
+ * - The caller must hold net->proc_cells_lock
+ */
+void afs_dynroot_rmdir(struct afs_net *net, struct afs_cell *cell)
+{
+	struct super_block *sb = net->dynroot_sb;
+	char *dotname = cell->name - 1;
+
+	if (!sb || atomic_read(&sb->s_active) == 0)
+		return;
+
+	inode_lock(sb->s_root->d_inode);
+	afs_dynroot_rm_one_dir(sb->s_root, cell->name, cell->name_len);
+	afs_dynroot_rm_one_dir(sb->s_root, dotname, cell->name_len + 1);
+	inode_unlock(sb->s_root->d_inode);
 	_leave("");
 }
 



From dhowells at redhat.com  Tue Jan  7 10:34:50 2025
From: dhowells at redhat.com (David Howells)
Date: Tue,  7 Jan 2025 18:34:50 +0000
Subject: [PATCH v2 2/3] afs: Add rootcell checks
In-Reply-To: <20250107183454.608451-1-dhowells@redhat.com>
References: <20250107183454.608451-1-dhowells@redhat.com>
Message-ID: <20250107183454.608451-3-dhowells@redhat.com>

Add some checks for the validity of the cell name.  It's may get put into a
symlink, so preclude it containing any slashes or "..".  Also disallow
starting/ending with a dot.  This makes /afs/@cell/ as a symlink less of a
security risk.

Also disallow multiple setting of /proc/net/afs/rootcell for any given
network namespace.  Once set, the value may not be changed.  This makes it
easier to only create /afs/@cell and /afs/. at cell if there's a rootcell.

Signed-off-by: David Howells <dhowells at redhat.com>
cc: Marc Dionne <marc.dionne at auristor.com>
cc: linux-afs at lists.infradead.org
---
 fs/afs/cell.c | 8 ++++++++
 fs/afs/proc.c | 8 +++++++-
 2 files changed, 15 insertions(+), 1 deletion(-)

diff --git a/fs/afs/cell.c b/fs/afs/cell.c
index 1aba6d4d03a9..cee42646736c 100644
--- a/fs/afs/cell.c
+++ b/fs/afs/cell.c
@@ -367,6 +367,14 @@ int afs_cell_init(struct afs_net *net, const char *rootcell)
 		len = cp - rootcell;
 	}
 
+	if (len == 0 || !rootcell[0] || rootcell[0] == '.' || rootcell[len - 1] == '.')
+		return -EINVAL;
+	if (memchr(rootcell, '/', len))
+		return -EINVAL;
+	cp = strstr(rootcell, "..");
+	if (cp && cp < rootcell + len)
+		return -EINVAL;
+
 	/* allocate a cell record for the root cell */
 	new_root = afs_lookup_cell(net, rootcell, len, vllist, false);
 	if (IS_ERR(new_root)) {
diff --git a/fs/afs/proc.c b/fs/afs/proc.c
index 15eab053af6d..e7614f4f30c2 100644
--- a/fs/afs/proc.c
+++ b/fs/afs/proc.c
@@ -240,7 +240,13 @@ static int afs_proc_rootcell_write(struct file *file, char *buf, size_t size)
 	/* determine command to perform */
 	_debug("rootcell=%s", buf);
 
-	ret = afs_cell_init(net, buf);
+	ret = -EEXIST;
+	inode_lock(file_inode(file));
+	if (!net->ws_cell)
+		ret = afs_cell_init(net, buf);
+	else
+		printk("busy\n");
+	inode_unlock(file_inode(file));
 
 out:
 	_leave(" = %d", ret);



From dhowells at redhat.com  Tue Jan  7 10:34:51 2025
From: dhowells at redhat.com (David Howells)
Date: Tue,  7 Jan 2025 18:34:51 +0000
Subject: [PATCH v2 3/3] afs: Make /afs/@cell and /afs/.@cell symlinks
In-Reply-To: <20250107183454.608451-1-dhowells@redhat.com>
References: <20250107183454.608451-1-dhowells@redhat.com>
Message-ID: <20250107183454.608451-4-dhowells@redhat.com>

Make /afs/@cell a symlink in the /afs dynamic root to match what other AFS
clients do rather than doing a substitution in the dentry name.  This has
the bonus of being tab-expandable also.

Further, provide a /afs/. at cell symlink to point to the dotted cell share.

Signed-off-by: David Howells <dhowells at redhat.com>
cc: Marc Dionne <marc.dionne at auristor.com>
cc: linux-afs at lists.infradead.org
---

Notes:
    Changes
    =======
    ver #2)
     - Directly create the symlink nodes rather than going through lookup.

 fs/afs/dynroot.c           | 177 +++++++++++++++++++++++++++----------
 include/trace/events/afs.h |   2 +
 2 files changed, 131 insertions(+), 48 deletions(-)

diff --git a/fs/afs/dynroot.c b/fs/afs/dynroot.c
index f80a4244b9d2..d8bf52f77d93 100644
--- a/fs/afs/dynroot.c
+++ b/fs/afs/dynroot.c
@@ -185,50 +185,6 @@ struct inode *afs_try_auto_mntpt(struct dentry *dentry, struct inode *dir)
 	return ret == -ENOENT ? NULL : ERR_PTR(ret);
 }
 
-/*
- * Look up @cell in a dynroot directory.  This is a substitution for the
- * local cell name for the net namespace.
- */
-static struct dentry *afs_lookup_atcell(struct dentry *dentry)
-{
-	struct afs_cell *cell;
-	struct afs_net *net = afs_d2net(dentry);
-	struct dentry *ret;
-	char *name;
-	int len;
-
-	if (!net->ws_cell)
-		return ERR_PTR(-ENOENT);
-
-	ret = ERR_PTR(-ENOMEM);
-	name = kmalloc(AFS_MAXCELLNAME + 1, GFP_KERNEL);
-	if (!name)
-		goto out_p;
-
-	down_read(&net->cells_lock);
-	cell = net->ws_cell;
-	if (cell) {
-		len = cell->name_len;
-		memcpy(name, cell->name, len + 1);
-	}
-	up_read(&net->cells_lock);
-
-	ret = ERR_PTR(-ENOENT);
-	if (!cell)
-		goto out_n;
-
-	ret = lookup_one_len(name, dentry->d_parent, len);
-
-	/* We don't want to d_add() the @cell dentry here as we don't want to
-	 * the cached dentry to hide changes to the local cell name.
-	 */
-
-out_n:
-	kfree(name);
-out_p:
-	return ret;
-}
-
 /*
  * Look up an entry in a dynroot directory.
  */
@@ -247,10 +203,6 @@ static struct dentry *afs_dynroot_lookup(struct inode *dir, struct dentry *dentr
 		return ERR_PTR(-ENAMETOOLONG);
 	}
 
-	if (dentry->d_name.len == 5 &&
-	    memcmp(dentry->d_name.name, "@cell", 5) == 0)
-		return afs_lookup_atcell(dentry);
-
 	return d_splice_alias(afs_try_auto_mntpt(dentry, dir), dentry);
 }
 
@@ -343,6 +295,131 @@ void afs_dynroot_rmdir(struct afs_net *net, struct afs_cell *cell)
 	_leave("");
 }
 
+static void afs_atcell_delayed_put_cell(void *arg)
+{
+	struct afs_cell *cell = arg;
+
+	afs_put_cell(cell, afs_cell_trace_put_atcell);
+}
+
+/*
+ * Read @cell or . at cell symlinks.
+ */
+static const char *afs_atcell_get_link(struct dentry *dentry, struct inode *inode,
+				       struct delayed_call *done)
+{
+	struct afs_vnode *vnode = AFS_FS_I(inode);
+	struct afs_cell *cell;
+	struct afs_net *net = afs_i2net(inode);
+	const char *name;
+	bool dotted = vnode->fid.vnode == 3;
+
+	if (!net->ws_cell)
+		return ERR_PTR(-ENOENT);
+
+	down_read(&net->cells_lock);
+
+	cell = net->ws_cell;
+	if (dotted)
+		name = cell->name - 1;
+	else
+		name = cell->name;
+	afs_get_cell(cell, afs_cell_trace_get_atcell);
+	set_delayed_call(done, afs_atcell_delayed_put_cell, cell);
+
+	up_read(&net->cells_lock);
+	return name;
+}
+
+static const struct inode_operations afs_atcell_inode_operations = {
+	.get_link	= afs_atcell_get_link,
+};
+
+/*
+ * Look up @cell or . at cell in a dynroot directory.  This is a substitution for
+ * the local cell name for the net namespace.
+ */
+static struct dentry *afs_dynroot_create_symlink(struct dentry *root, const char *name)
+{
+	struct afs_vnode *vnode;
+	struct afs_fid fid = { .vnode = 2, .unique = 1, };
+	struct dentry *dentry;
+	struct inode *inode;
+
+	if (name[0] == '.')
+		fid.vnode = 3;
+
+	dentry = d_alloc_name(root, name);
+	if (!dentry)
+		return ERR_PTR(-ENOMEM);
+
+	inode = iget5_locked(dentry->d_sb, fid.vnode,
+			     afs_iget5_pseudo_test, afs_iget5_pseudo_set, &fid);
+	if (!inode) {
+		dput(dentry);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	vnode = AFS_FS_I(inode);
+
+	/* there shouldn't be an existing inode */
+	if (WARN_ON_ONCE(!(inode->i_state & I_NEW))) {
+		iput(inode);
+		dput(dentry);
+		return ERR_PTR(-EIO);
+	}
+
+	netfs_inode_init(&vnode->netfs, NULL, false);
+	simple_inode_init_ts(inode);
+	set_nlink(inode, 1);
+	inode->i_size		= 0;
+	inode->i_mode		= S_IFLNK | 0555;
+	inode->i_op		= &afs_atcell_inode_operations;
+	inode->i_uid		= GLOBAL_ROOT_UID;
+	inode->i_gid		= GLOBAL_ROOT_GID;
+	inode->i_blocks		= 0;
+	inode->i_generation	= 0;
+	inode->i_flags		|= S_NOATIME;
+
+	unlock_new_inode(inode);
+	d_splice_alias(inode, dentry);
+	return dentry;
+}
+
+/*
+ * Create @cell and . at cell symlinks.
+ */
+static int afs_dynroot_symlink(struct afs_net *net)
+{
+	struct super_block *sb = net->dynroot_sb;
+	struct dentry *root, *symlink, *dsymlink;
+	int ret;
+
+	/* Let the ->lookup op do the creation */
+	root = sb->s_root;
+	inode_lock(root->d_inode);
+	symlink = afs_dynroot_create_symlink(root, "@cell");
+	if (IS_ERR(symlink)) {
+		ret = PTR_ERR(symlink);
+		goto unlock;
+	}
+
+	dsymlink = afs_dynroot_create_symlink(root, ". at cell");
+	if (IS_ERR(dsymlink)) {
+		ret = PTR_ERR(dsymlink);
+		dput(symlink);
+		goto unlock;
+	}
+
+	/* Note that we're retaining extra refs on the dentries. */
+	symlink->d_fsdata = (void *)1UL;
+	dsymlink->d_fsdata = (void *)1UL;
+	ret = 0;
+unlock:
+	inode_unlock(root->d_inode);
+	return ret;
+}
+
 /*
  * Populate a newly created dynamic root with cell names.
  */
@@ -355,6 +432,10 @@ int afs_dynroot_populate(struct super_block *sb)
 	mutex_lock(&net->proc_cells_lock);
 
 	net->dynroot_sb = sb;
+	ret = afs_dynroot_symlink(net);
+	if (ret < 0)
+		goto error;
+
 	hlist_for_each_entry(cell, &net->proc_cells, proc_link) {
 		ret = afs_dynroot_mkdir(net, cell);
 		if (ret < 0)
diff --git a/include/trace/events/afs.h b/include/trace/events/afs.h
index a0aed1a428a1..de0e2301a037 100644
--- a/include/trace/events/afs.h
+++ b/include/trace/events/afs.h
@@ -168,12 +168,14 @@ enum yfs_cm_operation {
 #define afs_cell_traces \
 	EM(afs_cell_trace_alloc,		"ALLOC     ") \
 	EM(afs_cell_trace_free,			"FREE      ") \
+	EM(afs_cell_trace_get_atcell,		"GET atcell") \
 	EM(afs_cell_trace_get_queue_dns,	"GET q-dns ") \
 	EM(afs_cell_trace_get_queue_manage,	"GET q-mng ") \
 	EM(afs_cell_trace_get_queue_new,	"GET q-new ") \
 	EM(afs_cell_trace_get_vol,		"GET vol   ") \
 	EM(afs_cell_trace_insert,		"INSERT    ") \
 	EM(afs_cell_trace_manage,		"MANAGE    ") \
+	EM(afs_cell_trace_put_atcell,		"PUT atcell") \
 	EM(afs_cell_trace_put_candidate,	"PUT candid") \
 	EM(afs_cell_trace_put_destroy,		"PUT destry") \
 	EM(afs_cell_trace_put_queue_work,	"PUT q-work") \



From brauner at kernel.org  Thu Jan  9 08:21:05 2025
From: brauner at kernel.org (Christian Brauner)
Date: Thu,  9 Jan 2025 17:21:05 +0100
Subject: [PATCH] afs: Fix merge preference rule failure condition
In-Reply-To: <529850.1736261552@warthog.procyon.org.uk>
References: <529850.1736261552@warthog.procyon.org.uk>
Message-ID: <20250109-rekord-drehkreuz-a91b866537db@brauner>

On Tue, 07 Jan 2025 14:52:32 +0000, David Howells wrote:
> syzbot reported a lock held when returning to userspace[1].  This is
> because if argc is less than 0 and the function returns directly, the held
> inode lock is not released.
> 
> Fix this by store the error in ret and jump to done to clean up instead of
> returning directly.
> 
> [...]

Applied to the vfs.fixes branch of the vfs/vfs.git tree.
Patches in the vfs.fixes branch should appear in linux-next soon.

Please report any outstanding bugs that were missed during review in a
new review to the original patch series allowing us to drop it.

It's encouraged to provide Acked-bys and Reviewed-bys even though the
patch has now been applied. If possible patch trailers will be updated.

Note that commit hashes shown below are subject to change due to rebase,
trailer updates or similar. If in doubt, please check the listed branch.

tree:   https://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs.git
branch: vfs.fixes

[1/1] afs: Fix merge preference rule failure condition
      https://git.kernel.org/vfs/vfs/c/31716216c799


From dhowells at redhat.com  Thu Jan  9 17:03:02 2025
From: dhowells at redhat.com (David Howells)
Date: Fri, 10 Jan 2025 01:03:02 +0000
Subject: [RFC PATCH 0/8] crypto: Add generic Kerberos library with crypto as AEAD algorithms
Message-ID: <20250110010313.1471063-1-dhowells@redhat.com>

Hi Herbert, Chuck,

Here's my next go at a generic Kerberos crypto library in the kernel so
that I can share code between rxrpc and sunrpc (and cifs?).

I derived some of the parts from the sunrpc gss library and added more
advanced AES and Camellia crypto.  The crypto bits are inside AEAD
algorithms as Herbert required, but there's also a library of supplementary
functions to aid in managing message layout.

You can use:

        const struct krb5_enctype *crypto_krb5_find_enctype(u32 enctype);

to go and get an information table and this will also let you get at the
name of the AEAD algorithm associated with that encoding type number.

Each AEAD algorithm is defined for a particular Kerberos 5 type by name
(not by enctype number) and supports both encryption and checksumming
(MIC) through the AEAD encrypt/decrypt request API.

Note that the plain text may be a different size to the cipher text and
this causes the testmgr some issues as it thinks the extra data is an auth
tag (but this doesn't want auth tags).

A kerberos AEAD object is configured through its setkey method and this
takes a compound structure that indicates the mode of operation (encrypt or
checksum), the usage type and either the transport key or the subkeys.  The
setkey method allocates and keys the constituent ciphers and hashes - but
that's a detail hidden inside the object.

This library has its own self-testing framework that checks more things
than is possible with the testmgr, including subkey derivation.  It also
checks things about the output of encrypt + decrypt that testmgr doesn't.
That said, testmgr is also provisioned with some encryption and
checksumming tests for Camilla and AES2.

Note that, for purposes of illustration, I've included some rxrpc patches
that use this interface to implement the rxgk Rx security class.  The
branch also is based on net-next that carries some rxrpc patches that are a
prerequisite for this, but the crypto patches don't need it.

---
The patches can be found here also:

	http://git.kernel.org/cgit/linux/kernel/git/dhowells/linux-fs.git/log/?h=crypto-krb5

David

David Howells (8):
  crypto/krb5: Add some constants out of sunrpc headers
  crypto/krb5: Provide Kerberos 5 crypto through AEAD API
  crypto/krb5: Test manager data
  rxrpc: Add the security index for yfs-rxgk
  rxrpc: Add YFS RxGK (GSSAPI) security class
  rxrpc: rxgk: Provide infrastructure and key derivation
  rxrpc: rxgk: Implement the yfs-rxgk security class (GSSAPI)
  rxrpc: rxgk: Implement connection rekeying

 crypto/Kconfig                   |    1 +
 crypto/Makefile                  |    2 +
 crypto/aead.c                    |    2 +
 crypto/krb5/Kconfig              |   24 +
 crypto/krb5/Makefile             |   17 +
 crypto/krb5/internal.h           |  162 ++++
 crypto/krb5/kdf.c                |  334 ++++++++
 crypto/krb5/krb5_aead.c          |  462 +++++++++++
 crypto/krb5/rfc3961_simplified.c |  815 +++++++++++++++++++
 crypto/krb5/rfc6803_camellia.c   |  190 +++++
 crypto/krb5/rfc8009_aes2.c       |  394 ++++++++++
 crypto/krb5/selftest.c           |  533 +++++++++++++
 crypto/krb5/selftest_data.c      |  370 +++++++++
 crypto/testmgr.c                 |   24 +
 crypto/testmgr.h                 |  456 +++++++++++
 fs/afs/misc.c                    |   13 +
 include/crypto/aead.h            |    2 +
 include/crypto/krb5.h            |  147 ++++
 include/keys/rxrpc-type.h        |   17 +
 include/trace/events/rxrpc.h     |   36 +
 include/uapi/linux/rxrpc.h       |   17 +
 net/rxrpc/Kconfig                |   10 +
 net/rxrpc/Makefile               |    5 +-
 net/rxrpc/ar-internal.h          |   22 +
 net/rxrpc/conn_event.c           |    2 +-
 net/rxrpc/conn_object.c          |    1 +
 net/rxrpc/key.c                  |  183 +++++
 net/rxrpc/output.c               |    2 +-
 net/rxrpc/protocol.h             |   20 +
 net/rxrpc/rxgk.c                 | 1244 ++++++++++++++++++++++++++++++
 net/rxrpc/rxgk_app.c             |  318 ++++++++
 net/rxrpc/rxgk_common.h          |   58 ++
 net/rxrpc/rxgk_kdf.c             |  260 +++++++
 net/rxrpc/rxkad.c                |    6 +-
 net/rxrpc/security.c             |    3 +
 35 files changed, 6147 insertions(+), 5 deletions(-)
 create mode 100644 crypto/krb5/Kconfig
 create mode 100644 crypto/krb5/Makefile
 create mode 100644 crypto/krb5/internal.h
 create mode 100644 crypto/krb5/kdf.c
 create mode 100644 crypto/krb5/krb5_aead.c
 create mode 100644 crypto/krb5/rfc3961_simplified.c
 create mode 100644 crypto/krb5/rfc6803_camellia.c
 create mode 100644 crypto/krb5/rfc8009_aes2.c
 create mode 100644 crypto/krb5/selftest.c
 create mode 100644 crypto/krb5/selftest_data.c
 create mode 100644 include/crypto/krb5.h
 create mode 100644 net/rxrpc/rxgk.c
 create mode 100644 net/rxrpc/rxgk_app.c
 create mode 100644 net/rxrpc/rxgk_common.h
 create mode 100644 net/rxrpc/rxgk_kdf.c



From dhowells at redhat.com  Thu Jan  9 17:03:03 2025
From: dhowells at redhat.com (David Howells)
Date: Fri, 10 Jan 2025 01:03:03 +0000
Subject: [RFC PATCH 1/8] crypto/krb5: Add some constants out of sunrpc headers
In-Reply-To: <20250110010313.1471063-1-dhowells@redhat.com>
References: <20250110010313.1471063-1-dhowells@redhat.com>
Message-ID: <20250110010313.1471063-2-dhowells@redhat.com>

Add some constants from the sunrpc headers.

Signed-off-by: David Howells <dhowells at redhat.com>
cc: Herbert Xu <herbert at gondor.apana.org.au>
cc: "David S. Miller" <davem at davemloft.net>
cc: Chuck Lever <chuck.lever at oracle.com>
cc: Marc Dionne <marc.dionne at auristor.com>
cc: Eric Dumazet <edumazet at google.com>
cc: Jakub Kicinski <kuba at kernel.org>
cc: Paolo Abeni <pabeni at redhat.com>
cc: Simon Horman <horms at kernel.org>
cc: linux-afs at lists.infradead.org
cc: linux-nfs at vger.kernel.org
cc: linux-crypto at vger.kernel.org
cc: netdev at vger.kernel.org
---
 include/crypto/krb5.h | 50 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 50 insertions(+)
 create mode 100644 include/crypto/krb5.h

diff --git a/include/crypto/krb5.h b/include/crypto/krb5.h
new file mode 100644
index 000000000000..05e80fad2b38
--- /dev/null
+++ b/include/crypto/krb5.h
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* Kerberos 5 crypto
+ *
+ * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells at redhat.com)
+ */
+
+#ifndef _CRYPTO_KRB5_H
+#define _CRYPTO_KRB5_H
+
+/* per Kerberos v5 protocol spec crypto types from the wire.
+ * these get mapped to linux kernel crypto routines.
+ */
+#define KRB5_ENCTYPE_NULL			0x0000
+#define KRB5_ENCTYPE_DES_CBC_CRC		0x0001	/* DES cbc mode with CRC-32 */
+#define KRB5_ENCTYPE_DES_CBC_MD4		0x0002	/* DES cbc mode with RSA-MD4 */
+#define KRB5_ENCTYPE_DES_CBC_MD5		0x0003	/* DES cbc mode with RSA-MD5 */
+#define KRB5_ENCTYPE_DES_CBC_RAW		0x0004	/* DES cbc mode raw */
+/* XXX deprecated? */
+#define KRB5_ENCTYPE_DES3_CBC_SHA		0x0005	/* DES-3 cbc mode with NIST-SHA */
+#define KRB5_ENCTYPE_DES3_CBC_RAW		0x0006	/* DES-3 cbc mode raw */
+#define KRB5_ENCTYPE_DES_HMAC_SHA1		0x0008
+#define KRB5_ENCTYPE_DES3_CBC_SHA1		0x0010
+#define KRB5_ENCTYPE_AES128_CTS_HMAC_SHA1_96	0x0011
+#define KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96	0x0012
+#define KRB5_ENCTYPE_ARCFOUR_HMAC		0x0017
+#define KRB5_ENCTYPE_ARCFOUR_HMAC_EXP		0x0018
+#define KRB5_ENCTYPE_UNKNOWN			0x01ff
+
+#define KRB5_CKSUMTYPE_CRC32			0x0001
+#define KRB5_CKSUMTYPE_RSA_MD4			0x0002
+#define KRB5_CKSUMTYPE_RSA_MD4_DES		0x0003
+#define KRB5_CKSUMTYPE_DESCBC			0x0004
+#define KRB5_CKSUMTYPE_RSA_MD5			0x0007
+#define KRB5_CKSUMTYPE_RSA_MD5_DES		0x0008
+#define KRB5_CKSUMTYPE_NIST_SHA			0x0009
+#define KRB5_CKSUMTYPE_HMAC_SHA1_DES3		0x000c
+#define KRB5_CKSUMTYPE_HMAC_SHA1_96_AES128	0x000f
+#define KRB5_CKSUMTYPE_HMAC_SHA1_96_AES256	0x0010
+#define KRB5_CKSUMTYPE_HMAC_MD5_ARCFOUR		-138 /* Microsoft md5 hmac cksumtype */
+
+/*
+ * Constants used for key derivation
+ */
+/* from rfc3961 */
+#define KEY_USAGE_SEED_CHECKSUM         (0x99)
+#define KEY_USAGE_SEED_ENCRYPTION       (0xAA)
+#define KEY_USAGE_SEED_INTEGRITY        (0x55)
+
+#endif /* _CRYPTO_KRB5_H */



From dhowells at redhat.com  Thu Jan  9 17:03:04 2025
From: dhowells at redhat.com (David Howells)
Date: Fri, 10 Jan 2025 01:03:04 +0000
Subject: [RFC PATCH 2/8] crypto/krb5: Provide Kerberos 5 crypto through AEAD API
In-Reply-To: <20250110010313.1471063-1-dhowells@redhat.com>
References: <20250110010313.1471063-1-dhowells@redhat.com>
Message-ID: <20250110010313.1471063-3-dhowells@redhat.com>

Use the AEAD crypto API to provide Kerberos 5 crypto, plus some
supplementary library functions that lie outside of the AEAD API.

The crypto algorithms only perform the actual crypto operations; they do
not do any laying out of the message and nor do they insert any metadata or
padding.  Everything is done by dead-reckoning as the AEAD API does not
provide a useful way to pass the extra parameters required.

When setting the key on a crypto algorithm, setkey takes a composite
structure consisting of an indication of the mode of transformation to be
applied to the message (checksum only or full encryption); the usage type
to be used in deriving the keys; an indicator indicating what key is being
presented (K0 or Kc/Ke+Ki); and the material for those key(s).  Based on
this, the setkey code allocates and keys the appropriate ciphers and
hashes.

When dispatching a request, both checksumming (MIC) and encryption use the
encrypt and decrypt methods.  A source message, prelaid out with
confounders or other metadata inserted is provided in the source buffer.
The cryptolen indicates the amount of source message data, not including
the trailer after the data (which includes the integrity checksum) and not
including any associated data.

Associated data is only used by checksumming encrypt/decrypt.  The
associated data is added to the checksum hash before the data in the
message, but does not occupy any part of the output message.

Authentication tags are not used at all and should cause EINVAL if used (a
later patch does that).

For the moment, the kerberos encryption algorithms use separate hash and
cipher algorithms internally, but should really use dual hash+cipher and
cipher+hash algorithms if possible to avoid doing these in series.  Offload
off this may be possible through something like the Intel QAT.

To help with managing the layout, a number of functions are also provided:

 (1) crypto_krb5_find_enctype() - Find the definition of an encoding type
     by protocol number.  This provides the name of the algorithm along
     with a host of parameter values, such as key sizes, block size, etc..

     Note that the enctype wraps the aead_alg struct so that the crypto
     routines can use container_of() to find it.

 (2) crypto_krb5_how_much_buffer() - Determine how much bufferage is needed
     for a certain amount of data.

 (3) crypto_krb5_how_much_data() - Determine how much data will fit into a
     certain amount of buffer.

 (4) crypto_krb5_where_is_the_data() - Determine where in a decrypted
     message the data is and how much data there is.  This may, in future,
     need to access the contents of the decrypted message to access
     metadata.

 (5) crypto_krb5_confound_buffer() - Insert the confounder in the buffer at
     the appropriate place - or generate one randomly.  This may, in
     future, be combined with something that inserts padding and metadata.

And to help with key derivation:

 (6) crypto_krb5_calc_PRFplus() - Calculate the PRF+ function.

This patch includes the following features:

 (1) The PRF+ function from RFC4402 used in deriving keys.

 (2) The RFC3961 simplified crypto profile for Kerberos 5 rfc3961 with the
     pseudo-random function, PRF(), from section 5.3 and the key derivation
     function, DK() from section 5.1.

 (3) Message encryption and decryption according to RFC3961 sec 5.3.

 (4) Message checksumming and verification according to RFC3961 sec 5.4.

 (5) The aes128-cts-hmac-sha1-96 and aes256-cts-hmac-sha1-96 enctypes from
     RFC3962, using the RFC3961 kerberos 5 simplified crypto scheme.

 (6) The aes128-cts-hmac-sha256-128 and aes256-cts-hmac-sha384-192 enctypes
     from RFC8009 (which override the rfc3961 kerberos 5 simplified crypto
     scheme).

 (7) The camellia128-cts-cmac and camellia256-cts-cmac enctypes from
     RFC6803.

     Note that the test vectors in rfc6803 for encryption are incomplete,
     lacking the key usage number needed to derive Ke and Ki, and there are
     errata for this:

	https://www.rfc-editor.org/errata_search.php?rfc=6803

 (8) Functions to find out about the layout of the crypto section.  One
     calculates, for a given size of data, how big a buffer will be
     required to hold it and where the data will be within it.  The other
     calculates, for an amount of buffer, what's the maximum size of data
     that will fit therein, and where it will start.

 (9) Self-testing infrastructure to test the pseudo-random function, key
     derivation, encryption and checksumming.

Signed-off-by: David Howells <dhowells at redhat.com>
cc: Herbert Xu <herbert at gondor.apana.org.au>
cc: "David S. Miller" <davem at davemloft.net>
cc: Chuck Lever <chuck.lever at oracle.com>
cc: Marc Dionne <marc.dionne at auristor.com>
cc: Eric Dumazet <edumazet at google.com>
cc: Jakub Kicinski <kuba at kernel.org>
cc: Paolo Abeni <pabeni at redhat.com>
cc: Simon Horman <horms at kernel.org>
cc: linux-afs at lists.infradead.org
cc: linux-nfs at vger.kernel.org
cc: linux-crypto at vger.kernel.org
cc: netdev at vger.kernel.org
---
 crypto/Kconfig                   |   1 +
 crypto/Makefile                  |   2 +
 crypto/krb5/Kconfig              |  24 +
 crypto/krb5/Makefile             |  17 +
 crypto/krb5/internal.h           | 162 ++++++
 crypto/krb5/kdf.c                | 334 +++++++++++++
 crypto/krb5/krb5_aead.c          | 456 +++++++++++++++++
 crypto/krb5/rfc3961_simplified.c | 815 +++++++++++++++++++++++++++++++
 crypto/krb5/rfc6803_camellia.c   | 190 +++++++
 crypto/krb5/rfc8009_aes2.c       | 394 +++++++++++++++
 crypto/krb5/selftest.c           | 533 ++++++++++++++++++++
 crypto/krb5/selftest_data.c      | 370 ++++++++++++++
 include/crypto/krb5.h            | 101 +++-
 13 files changed, 3397 insertions(+), 2 deletions(-)
 create mode 100644 crypto/krb5/Kconfig
 create mode 100644 crypto/krb5/Makefile
 create mode 100644 crypto/krb5/internal.h
 create mode 100644 crypto/krb5/kdf.c
 create mode 100644 crypto/krb5/krb5_aead.c
 create mode 100644 crypto/krb5/rfc3961_simplified.c
 create mode 100644 crypto/krb5/rfc6803_camellia.c
 create mode 100644 crypto/krb5/rfc8009_aes2.c
 create mode 100644 crypto/krb5/selftest.c
 create mode 100644 crypto/krb5/selftest_data.c

diff --git a/crypto/Kconfig b/crypto/Kconfig
index 6b0bfbccac08..7f1a60065dc1 100644
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -1477,5 +1477,6 @@ endif
 source "drivers/crypto/Kconfig"
 source "crypto/asymmetric_keys/Kconfig"
 source "certs/Kconfig"
+source "crypto/krb5/Kconfig"
 
 endif	# if CRYPTO
diff --git a/crypto/Makefile b/crypto/Makefile
index 77abca715445..2d1463581d10 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -213,3 +213,5 @@ obj-$(CONFIG_CRYPTO_SIMD) += crypto_simd.o
 # Key derivation function
 #
 obj-$(CONFIG_CRYPTO_KDF800108_CTR) += kdf_sp800108.o
+
+obj-$(CONFIG_CRYPTO_KRB5) += krb5/
diff --git a/crypto/krb5/Kconfig b/crypto/krb5/Kconfig
new file mode 100644
index 000000000000..0f8df83ff4fd
--- /dev/null
+++ b/crypto/krb5/Kconfig
@@ -0,0 +1,24 @@
+config CRYPTO_KRB5
+	tristate "Kerberos 5 crypto"
+	select CRYPTO_MANAGER
+	select CRYPTO_SKCIPHER
+	select CRYPTO_HASH_INFO
+	select CRYPTO_HMAC
+	select CRYPTO_CMAC
+	select CRYPTO_SHA1
+	select CRYPTO_SHA256
+	select CRYPTO_SHA512
+	select CRYPTO_CBC
+	select CRYPTO_CTS
+	select CRYPTO_AES
+	select CRYPTO_CAMELLIA
+	help
+	  Provide a library for provision of Kerberos-5-based crypto.  This is
+	  intended for network filesystems to use.
+
+config CRYPTO_KRB5_SELFTESTS
+	bool "Kerberos 5 crypto selftests"
+	depends on CRYPTO_KRB5
+	help
+	  Turn on some self-testing for the kerberos 5 crypto functions.  These
+	  will be performed on module load or boot, if compiled in.
diff --git a/crypto/krb5/Makefile b/crypto/krb5/Makefile
new file mode 100644
index 000000000000..65cb211aebeb
--- /dev/null
+++ b/crypto/krb5/Makefile
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for asymmetric cryptographic keys
+#
+
+krb5-y += \
+	kdf.o \
+	krb5_aead.o \
+	rfc3961_simplified.o \
+	rfc6803_camellia.o \
+	rfc8009_aes2.o
+
+krb5-$(CONFIG_CRYPTO_KRB5_SELFTESTS) += \
+	selftest.o \
+	selftest_data.o
+
+obj-$(CONFIG_CRYPTO_KRB5) += krb5.o
diff --git a/crypto/krb5/internal.h b/crypto/krb5/internal.h
new file mode 100644
index 000000000000..6ae7ff7008a8
--- /dev/null
+++ b/crypto/krb5/internal.h
@@ -0,0 +1,162 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* Kerberos5 crypto internals
+ *
+ * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells at redhat.com)
+ */
+
+#include <linux/scatterlist.h>
+#include <crypto/krb5.h>
+#include <crypto/hash.h>
+#include <crypto/skcipher.h>
+#include <crypto/internal/aead.h>
+
+/*
+ * Checksum, encryption and integrity keyed algorithms for Kerberos encryption
+ * or checksumming.
+ */
+struct krb5_ctx {
+	struct crypto_sync_skcipher	*Ke; /* Encryption key (fully encrypted) */
+	union {
+		struct crypto_shash	*Ki; /* Integrity key (fully encrypted) */
+		struct crypto_shash	*Kc; /* Checksum key (checksummed only) */
+	};
+};
+
+static inline struct krb5_ctx *crypto_krb5_ctx(struct crypto_aead *tfm)
+{
+	return crypto_aead_ctx(tfm);
+}
+
+/*
+ * Profile used for key derivation and encryption.
+ */
+struct krb5_crypto_profile {
+	 /* Pseudo-random function */
+	int (*calc_PRF)(const struct krb5_enctype *krb5,
+			const struct krb5_buffer *protocol_key,
+			const struct krb5_buffer *octet_string,
+			struct krb5_buffer *result,
+			gfp_t gfp);
+
+	/* Checksum key derivation */
+	int (*calc_Kc)(const struct krb5_enctype *krb5,
+		       const struct krb5_buffer *TK,
+		       const struct krb5_buffer *usage_constant,
+		       struct krb5_buffer *Kc,
+		       gfp_t gfp);
+
+	/* Encryption key derivation */
+	int (*calc_Ke)(const struct krb5_enctype *krb5,
+		       const struct krb5_buffer *TK,
+		       const struct krb5_buffer *usage_constant,
+		       struct krb5_buffer *Ke,
+		       gfp_t gfp);
+
+	 /* Integrity key derivation */
+	int (*calc_Ki)(const struct krb5_enctype *krb5,
+		       const struct krb5_buffer *TK,
+		       const struct krb5_buffer *usage_constant,
+		       struct krb5_buffer *Ki,
+		       gfp_t gfp);
+};
+
+/*
+ * Crypto size/alignment rounding convenience macros.
+ */
+#define crypto_roundup(X) ((unsigned int)round_up((X), CRYPTO_MINALIGN))
+
+#define krb5_shash_size(TFM) \
+	crypto_roundup(sizeof(struct shash_desc) + crypto_shash_descsize(TFM))
+#define krb5_skcipher_size(TFM) \
+	crypto_roundup(sizeof(struct skcipher_request) + crypto_skcipher_reqsize(TFM))
+#define krb5_digest_size(TFM) \
+	crypto_roundup(crypto_shash_digestsize(TFM))
+#define krb5_sync_skcipher_size(TFM) \
+	krb5_skcipher_size(&(TFM)->base)
+#define krb5_sync_skcipher_ivsize(TFM) \
+	crypto_roundup(crypto_sync_skcipher_ivsize(TFM))
+#define round16(x) (((x) + 15) & ~15)
+
+/*
+ * Self-testing data.
+ */
+struct krb5_prf_test {
+	u32 etype;
+	const char *name, *key, *octet, *prf;
+};
+
+struct krb5_key_test_one {
+	u32 use;
+	const char *key;
+};
+
+struct krb5_key_test {
+	u32 etype;
+	const char *name, *key;
+	struct krb5_key_test_one Kc, Ke, Ki;
+};
+
+struct krb5_enc_test {
+	u32 etype;
+	const char *name, *plain, *conf, *key, *ct;
+};
+
+struct krb5_mic_test {
+	u32 etype;
+	const char *name, *plain, *key, *mic;
+};
+
+/*
+ * kdf.c
+ */
+int krb5_setkey(struct crypto_aead *aead, const u8 *key, unsigned int keylen);
+int krb5_derive_Kc(const struct krb5_enctype *krb5, const struct krb5_buffer *TK,
+		   u32 usage, struct krb5_buffer *key, gfp_t gfp);
+int krb5_derive_Ke(const struct krb5_enctype *krb5, const struct krb5_buffer *TK,
+		   u32 usage, struct krb5_buffer *key, gfp_t gfp);
+int krb5_derive_Ki(const struct krb5_enctype *krb5, const struct krb5_buffer *TK,
+		   u32 usage, struct krb5_buffer *key, gfp_t gfp);
+
+/*
+ * rfc3961_simplified.c
+ */
+extern const struct krb5_crypto_profile rfc3961_simplified_profile;
+
+size_t sg_count(struct scatterlist *sg, int *_nents);
+int crypto_shash_update_sg(struct shash_desc *desc, struct scatterlist *sg,
+			   size_t offset, size_t len);
+int rfc3961_get_mic(struct aead_request *req);
+int rfc3961_verify_mic(struct aead_request *req);
+int rfc3961_aead_encrypt(struct aead_request *req);
+int rfc3961_aead_decrypt(struct aead_request *req);
+
+/*
+ * rfc6803_camellia.c
+ */
+extern const struct krb5_crypto_profile rfc6803_crypto_profile;
+
+/*
+ * rfc8009_aes2.c
+ */
+extern const struct krb5_crypto_profile rfc8009_crypto_profile;
+
+int rfc8009_aead_encrypt(struct aead_request *req);
+int rfc8009_aead_decrypt(struct aead_request *req);
+
+/*
+ * selftest.c
+ */
+#ifdef CONFIG_CRYPTO_KRB5_SELFTESTS
+int krb5_selftest(void);
+#else
+static inline int krb5_selftest(void) { return 0; }
+#endif
+
+/*
+ * selftest_data.c
+ */
+extern const struct krb5_prf_test krb5_prf_tests[];
+extern const struct krb5_key_test krb5_key_tests[];
+extern const struct krb5_enc_test krb5_enc_tests[];
+extern const struct krb5_mic_test krb5_mic_tests[];
diff --git a/crypto/krb5/kdf.c b/crypto/krb5/kdf.c
new file mode 100644
index 000000000000..b29e69f3d7f0
--- /dev/null
+++ b/crypto/krb5/kdf.c
@@ -0,0 +1,334 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Kerberos key derivation.
+ *
+ * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells at redhat.com)
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <crypto/skcipher.h>
+#include <crypto/hash.h>
+#include "internal.h"
+
+/**
+ * crypto_krb5_calc_PRFplus - Calculate PRF+ [RFC4402]
+ * @krb5: The encryption type to use
+ * @K: The protocol key for the pseudo-random function
+ * @L: The length of the output
+ * @S: The input octet string
+ * @result: Result buffer, sized to krb5->prf_len
+ * @gfp: Allocation restrictions
+ *
+ * Calculate the kerberos pseudo-random function, PRF+() by the following
+ * method:
+ *
+ *      PRF+(K, L, S) = truncate(L, T1 || T2 || .. || Tn)
+ *      Tn = PRF(K, n || S)
+ *      [rfc4402 sec 2]
+ */
+int crypto_krb5_calc_PRFplus(const struct krb5_enctype *krb5,
+			     const struct krb5_buffer *K,
+			     unsigned int L,
+			     const struct krb5_buffer *S,
+			     struct krb5_buffer *result,
+			     gfp_t gfp)
+{
+	struct krb5_buffer T_series, Tn, n_S;
+	void *buffer;
+	int ret, n = 1;
+
+	Tn.len = krb5->prf_len;
+	T_series.len = 0;
+	n_S.len = 4 + S->len;
+
+	buffer = kzalloc(round16(L + Tn.len) + round16(n_S.len), gfp);
+	if (!buffer)
+		return -ENOMEM;
+
+	T_series.data = buffer;
+	n_S.data = buffer + round16(L + Tn.len);
+	memcpy(n_S.data + 4, S->data, S->len);
+
+	while (T_series.len < L) {
+		*(__be32 *)(n_S.data) = htonl(n);
+		Tn.data = T_series.data + Tn.len * (n - 1);
+		ret = krb5->profile->calc_PRF(krb5, K, &n_S, &Tn, gfp);
+		if (ret < 0)
+			goto err;
+		T_series.len += Tn.len;
+		n++;
+	}
+
+	/* Truncate to L */
+	memcpy(result->data, T_series.data, L);
+	ret = 0;
+
+err:
+	kfree_sensitive(buffer);
+	return ret;
+}
+EXPORT_SYMBOL(crypto_krb5_calc_PRFplus);
+
+/**
+ * krb5_derive_Kc - Derive key Kc and install into a hash
+ * @krb5: The encryption type to use
+ * @TK: The base key
+ * @usage: The key usage number
+ * @key: Prepped (temporary) buffer to store the key into
+ * @gfp: Allocation restrictions
+ *
+ * Derive the Kerberos Kc checksumming key.  The key is stored into the
+ * prepared buffer.
+ */
+int krb5_derive_Kc(const struct krb5_enctype *krb5, const struct krb5_buffer *TK,
+		   u32 usage, struct krb5_buffer *key, gfp_t gfp)
+{
+	u8 buf[5] __aligned(CRYPTO_MINALIGN);
+	struct krb5_buffer usage_constant = { .len = 5, .data = buf };
+
+	*(__be32 *)buf = cpu_to_be32(usage);
+	buf[4] = KEY_USAGE_SEED_CHECKSUM;
+
+	key->len = krb5->Kc_len;
+	return krb5->profile->calc_Kc(krb5, TK, &usage_constant, key, gfp);
+}
+
+static int krb5_get_Kc(const struct krb5_enctype *krb5, const struct krb5_buffer *key,
+		       struct krb5_ctx *ctx, gfp_t gfp)
+{
+	struct crypto_shash *shash;
+
+	shash = crypto_alloc_shash(krb5->cksum_name, 0, 0);
+	if (IS_ERR(shash))
+		return (PTR_ERR(shash) == -ENOENT) ? -ENOPKG : PTR_ERR(shash);
+	ctx->Kc = shash;
+	return crypto_shash_setkey(shash, key->data, key->len);
+}
+
+/**
+ * krb5_derive_Ke - Derive key Ke and install into an skcipher
+ * @krb5: The encryption type to use
+ * @TK: The base key
+ * @usage: The key usage number
+ * @key: Prepped (temporary) buffer to store the key into
+ * @gfp: Allocation restrictions
+ *
+ * Derive the Kerberos Ke encryption key.  The key is stored into the prepared
+ * buffer.
+ */
+int krb5_derive_Ke(const struct krb5_enctype *krb5, const struct krb5_buffer *TK,
+		   u32 usage, struct krb5_buffer *key, gfp_t gfp)
+{
+	u8 buf[5] __aligned(CRYPTO_MINALIGN);
+	struct krb5_buffer usage_constant = { .len = 5, .data = buf };
+
+	*(__be32 *)buf = cpu_to_be32(usage);
+	buf[4] = KEY_USAGE_SEED_ENCRYPTION;
+
+	key->len = krb5->Ke_len;
+	return krb5->profile->calc_Ke(krb5, TK, &usage_constant, key, gfp);
+}
+
+static int krb5_get_Ke(const struct krb5_enctype *krb5, const struct krb5_buffer *key,
+		       struct krb5_ctx *ctx, gfp_t gfp)
+{
+	struct crypto_sync_skcipher *ci;
+
+	ci = crypto_alloc_sync_skcipher(krb5->encrypt_name, 0, 0);
+	if (IS_ERR(ci))
+		return (PTR_ERR(ci) == -ENOENT) ? -ENOPKG : PTR_ERR(ci);
+	ctx->Ke = ci;
+	return crypto_sync_skcipher_setkey(ci, key->data, key->len);
+}
+
+/**
+ * krb5_derive_Ki - Derive key Ki and install into a hash
+ * @krb5: The encryption type to use
+ * @TK: The base key
+ * @usage: The key usage number
+ * @key: Prepped (temporary) buffer to store the key into
+ * @gfp: Allocation restrictions
+ *
+ * Derive the Kerberos Ki integrity checksum key.  The key is stored into the
+ * prepared buffer.
+ */
+int krb5_derive_Ki(const struct krb5_enctype *krb5, const struct krb5_buffer *TK,
+		   u32 usage, struct krb5_buffer *key, gfp_t gfp)
+{
+	u8 buf[5] __aligned(CRYPTO_MINALIGN);
+	struct krb5_buffer usage_constant = { .len = 5, .data = buf };
+
+	*(__be32 *)buf = cpu_to_be32(usage);
+	buf[4] = KEY_USAGE_SEED_INTEGRITY;
+
+	key->len = krb5->Ki_len;
+	return krb5->profile->calc_Ki(krb5, TK, &usage_constant, key, gfp);
+}
+
+static int krb5_get_Ki(const struct krb5_enctype *krb5, const struct krb5_buffer *key,
+		       struct krb5_ctx *ctx, gfp_t gfp)
+{
+	struct crypto_shash *shash;
+
+	shash = crypto_alloc_shash(krb5->cksum_name, 0, 0);
+	if (IS_ERR(shash))
+		return (PTR_ERR(shash) == -ENOENT) ? -ENOPKG : PTR_ERR(shash);
+	ctx->Ki = shash;
+	return crypto_shash_setkey(shash, key->data, key->len);
+}
+
+/**
+ * krb5_setkey - Use the key material to derive keys
+ * @aead: The crypto algorithm context to configure
+ * @key: Compound of the key material and config parameters
+ * @keylen: Length of @key buffer
+ *
+ * Use the key material and configuration parameters to derive the appropriate
+ * keys for encrypting and/or checksumming.  The key material must be in one of
+ * the following formats:
+ *
+ * Checksummed mode:
+ *
+ *	KRB5_CHECKSUM_MODE (__be32) || usage (__be32) || transport key data (TK)
+ *	KRB5_CHECKSUM_MODE_KC (__be32) || usage (__be32) || Kc
+ *
+ * Full encryption mode:
+ *
+ *	KRB5_ENCRYPT_MODE (__be32) || usage (__be32) || transport key data (TK)
+ *	KRB5_ENCRYPT_MODE_KEKI (__be32) || usage (__be32) || Ke || Ki
+ */
+int krb5_setkey(struct crypto_aead *aead, const u8 *key, unsigned int keylen)
+{
+	const struct krb5_enctype *krb5 = crypto_krb5_enctype(aead);
+	struct krb5_buffer TK, keybuf = {}, Kn;
+	struct krb5_ctx *ctx = crypto_krb5_ctx(aead);
+	enum krb5_crypto_mode mode;
+	gfp_t gfp = GFP_NOFS; // TODO: The crypto API should provide this.
+	u32 usage;
+	int ret;
+
+	pr_debug("setkey %x %*phN\n", keylen, keylen, key);
+
+	if (keylen < 12)
+		return -EINVAL;
+
+	switch (mode) {
+	case KRB5_CHECKSUM_MODE:
+	case KRB5_ENCRYPT_MODE:
+		keybuf.data = kzalloc(krb5->key_bytes, gfp);
+		if (!keybuf.data)
+			return -ENOMEM;
+		break;
+	default:
+		break;
+	}
+
+	mode = get_unaligned_be32((__be32 *)key);
+	key += 4;
+	usage = get_unaligned_be32((__be32 *)key);
+	key += 4;
+	keylen -= 8;
+
+	switch (mode) {
+	case KRB5_CHECKSUM_MODE:
+		if (keylen != krb5->key_len)
+			return -EINVAL;
+		TK.data = (u8 *)key;
+		TK.len = keylen;
+		ret = krb5_derive_Kc(krb5, &TK, usage, &keybuf, gfp);
+		if (ret < 0)
+			goto err;
+		ret = krb5_get_Kc(krb5, &keybuf, ctx, gfp);
+		if (ret < 0)
+			goto err;
+		break;
+
+	case KRB5_CHECKSUM_MODE_KC:
+		if (keylen != krb5->Kc_len)
+			return -EINVAL;
+		Kn.data = (u8 *)key;
+		Kn.len = keylen;
+		ret = krb5_get_Kc(krb5, &Kn, ctx, gfp);
+		if (ret < 0)
+			goto err;
+		break;
+
+	case KRB5_ENCRYPT_MODE:
+		if (keylen != krb5->key_len)
+			return -EINVAL;
+		TK.data = (u8 *)key;
+		TK.len = keylen;
+		ret = krb5_derive_Ke(krb5, &TK, usage, &keybuf, gfp);
+		if (ret < 0) {
+			pr_err("get_Ke failed %d\n", ret);
+			goto err;
+		}
+		ret = krb5_get_Ke(krb5, &keybuf, ctx, gfp);
+		if (ret < 0) {
+			pr_err("get_Ke failed %d\n", ret);
+			goto err;
+		}
+		ret = krb5_derive_Ki(krb5, &TK, usage, &keybuf, gfp);
+		if (ret < 0) {
+			pr_err("get_Ki failed %d\n", ret);
+			goto err;
+		}
+		ret = krb5_get_Ki(krb5, &keybuf, ctx, gfp);
+		if (ret < 0) {
+			pr_err("get_Ki failed %d\n", ret);
+			goto err;
+		}
+		break;
+
+	case KRB5_ENCRYPT_MODE_KEKI:
+		if (keylen != krb5->Ke_len + krb5->Ki_len)
+			return -EINVAL;
+		Kn.data = (u8 *)key;
+		Kn.len = krb5->Ke_len;
+		ret = krb5_get_Ke(krb5, &Kn, ctx, gfp);
+		if (ret < 0) {
+			pr_err("get_Ke failed %d\n", ret);
+			goto err;
+		}
+		Kn.data = (u8 *)key + krb5->Ke_len;
+		Kn.len = krb5->Ki_len;
+		ret = krb5_get_Ki(krb5, &Kn, ctx, gfp);
+		if (ret < 0) {
+			pr_err("get_Ki failed %d\n", ret);
+			goto err;
+		}
+		break;
+
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	if (ctx->Ke && crypto_sync_skcipher_blocksize(ctx->Ke) != krb5->block_len) {
+		pr_notice("skcipher inconsistent with krb5 table %u!=%u\n",
+			  crypto_sync_skcipher_blocksize(ctx->Ke), krb5->block_len);
+		ret = -EINVAL;
+		goto err;
+	}
+
+	if (ctx->Ki && crypto_shash_digestsize(ctx->Ki) < krb5->cksum_len) {
+		pr_notice("hash inconsistent with krb5 table %u!=%u\n",
+			  crypto_shash_digestsize(ctx->Ki), krb5->cksum_len);
+		ret = -EINVAL;
+		goto err;
+	}
+
+out:
+	kfree(keybuf.data);
+	return ret;
+
+err:
+	if (ctx->Ke)
+		crypto_free_sync_skcipher(ctx->Ke);
+	if (ctx->Ki)
+		crypto_free_shash(ctx->Ki);
+	goto out;
+}
diff --git a/crypto/krb5/krb5_aead.c b/crypto/krb5/krb5_aead.c
new file mode 100644
index 000000000000..2c8b3921e976
--- /dev/null
+++ b/crypto/krb5/krb5_aead.c
@@ -0,0 +1,456 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Kerberos 5 crypto library.
+ *
+ * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells at redhat.com)
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/scatterlist.h>
+#include "internal.h"
+
+#include <crypto/algapi.h>
+#include <crypto/internal/aead.h>
+#include <crypto/internal/skcipher.h>
+
+/**
+ * crypto_krb5_how_much_buffer - Work out how much buffer is required for an amount of data
+ * @krb5: The encoding to use.
+ * @mode: The mode in which to operated (checksum/encrypt)
+ * @data_size: How much data we want to allow for
+ * @_offset: Where to place the offset into the buffer
+ *
+ * Calculate how much buffer space is required to wrap a given amount of data.
+ * This allows for a confounder, padding and checksum as appropriate.  The
+ * amount of buffer required is returned and the offset into the buffer at
+ * which the data will start is placed in *_offset.
+ */
+size_t crypto_krb5_how_much_buffer(const struct krb5_enctype *krb5,
+				   enum krb5_crypto_mode mode,
+				   size_t data_size, size_t *_offset)
+{
+	switch (mode) {
+	case KRB5_CHECKSUM_MODE:
+	case KRB5_CHECKSUM_MODE_KC:
+		*_offset = krb5->cksum_len;
+		return krb5->cksum_len + data_size;
+
+	case KRB5_ENCRYPT_MODE:
+	case KRB5_ENCRYPT_MODE_KEKI:
+		*_offset = krb5->conf_len;
+		return krb5->conf_len + data_size + krb5->cksum_len;
+
+	default:
+		WARN_ON(1);
+		*_offset = 0;
+		return 0;
+	}
+}
+EXPORT_SYMBOL(crypto_krb5_how_much_buffer);
+
+/**
+ * crypto_krb5_how_much_data - Work out how much data can fit in an amount of buffer
+ * @krb5: The encoding to use.
+ * @mode: The mode in which to operated (checksum/encrypt)
+ * @_buffer_size: How much buffer we want to allow for (may be reduced)
+ * @_offset: Where to place the offset into the buffer
+ *
+ * Calculate how much data can be fitted into given amount of buffer.  This
+ * allows for a confounder, padding and checksum as appropriate.  The amount of
+ * data that will fit is returned, the amount of buffer required is shrunk to
+ * allow for alignment and the offset into the buffer at which the data will
+ * start is placed in *_offset.
+ */
+size_t crypto_krb5_how_much_data(const struct krb5_enctype *krb5,
+				 enum krb5_crypto_mode mode,
+				 size_t *_buffer_size, size_t *_offset)
+{
+	size_t buffer_size = *_buffer_size, data_size;
+
+	switch (mode) {
+	case KRB5_CHECKSUM_MODE:
+		if (WARN_ON(buffer_size < krb5->cksum_len + 1))
+			goto bad;
+		*_offset = krb5->cksum_len;
+		return buffer_size - krb5->cksum_len;
+
+	case KRB5_ENCRYPT_MODE:
+		if (WARN_ON(buffer_size < krb5->conf_len + 1 + krb5->cksum_len))
+			goto bad;
+		data_size = buffer_size - krb5->cksum_len;
+		*_offset = krb5->conf_len;
+		return data_size - krb5->conf_len;
+
+	default:
+		WARN_ON(1);
+		goto bad;
+	}
+
+bad:
+	*_offset = 0;
+	return 0;
+}
+EXPORT_SYMBOL(crypto_krb5_how_much_data);
+
+/**
+ * crypto_krb5_where_is_the_data - Find the data in a decrypted message
+ * @krb5: The encoding to use.
+ * @mode: Mode of operation
+ * @_offset: Offset of the secure blob in the buffer; updated to data offset.
+ * @_len: The length of the secure blob; updated to data length.
+ *
+ * Find the offset and size of the data in a secure message so that this
+ * information can be used in the metadata buffer which will get added to the
+ * digest by crypto_krb5_verify_mic().
+ */
+void crypto_krb5_where_is_the_data(const struct krb5_enctype *krb5,
+				   enum krb5_crypto_mode mode,
+				   size_t *_offset, size_t *_len)
+{
+	switch (mode) {
+	case KRB5_CHECKSUM_MODE:
+	case KRB5_CHECKSUM_MODE_KC:
+		*_offset += krb5->cksum_len;
+		*_len -= krb5->cksum_len;
+		return;
+	case KRB5_ENCRYPT_MODE:
+	case KRB5_ENCRYPT_MODE_KEKI:
+		*_offset += krb5->conf_len;
+		*_len -= krb5->conf_len + krb5->cksum_len;
+		return;
+	default:
+		WARN_ON_ONCE(1);
+		return;
+	}
+}
+EXPORT_SYMBOL(crypto_krb5_where_is_the_data);
+
+/*
+ * crypto_krb5_confound_buffer - Insert confounder
+ * @krb5: The encoding type
+ * @sg: The buffer holding the message
+ * @nr_sg: Number of segments in @sg
+ * @confounder: The confounder to insert (or NULL for random confounder)
+ * @conf_len: Length of @confounder content (if not NULL)
+ * @msg_offset: Offset in buffer of start of message
+ *
+ * Insert an appropriately-sized confounder into a buffer at the correct place
+ * with respect to the start of the message.
+ */
+int crypto_krb5_confound_buffer(const struct krb5_enctype *krb5,
+				struct scatterlist *sg, unsigned int nr_sg,
+				const u8 *confounder, size_t conf_len,
+				size_t msg_offset)
+{
+	size_t done;
+	void *buffer = NULL;
+
+	if (!confounder) {
+		buffer = kmalloc(krb5->conf_len, GFP_NOFS);
+		if (!buffer)
+			return -ENOMEM;
+		get_random_bytes(buffer, krb5->conf_len);
+		confounder = buffer;
+	} else {
+		if (WARN_ON(conf_len != krb5->conf_len))
+			return -EFAULT;
+	}
+
+	done = sg_pcopy_from_buffer(sg, nr_sg, confounder, krb5->conf_len,
+				    msg_offset);
+	kfree(buffer);
+	return (done == krb5->conf_len) ? 0 : -EFAULT;
+}
+EXPORT_SYMBOL(crypto_krb5_confound_buffer);
+
+/*
+ * Table of supported Kerberos-V encryption types and their parameters.
+ */
+static struct krb5_enctype krb5_enctypes[] = {
+	{
+		.etype			= KRB5_ENCTYPE_AES128_CTS_HMAC_SHA1_96,
+		.ctype			= KRB5_CKSUMTYPE_HMAC_SHA1_96_AES128,
+		.name			= "aes128-cts-hmac-sha1-96",
+		.encrypt_name		= "cts(cbc(aes))",
+		.cksum_name		= "hmac(sha1)",
+		.hash_name		= "sha1",
+		.key_bytes		= 16,
+		.key_len		= 16,
+		.Kc_len			= 16,
+		.Ke_len			= 16,
+		.Ki_len			= 16,
+		.block_len		= 16,
+		.conf_len		= 16,
+		.cksum_len		= 12,
+		.hash_len		= 20,
+		.prf_len		= 16,
+		.keyed_cksum		= true,
+		.random_to_key		= NULL, /* Identity */
+		.profile		= &rfc3961_simplified_profile,
+
+		.aead.setkey		= krb5_setkey,
+		.aead.setauthsize	= NULL,
+		.aead.encrypt		= rfc3961_aead_encrypt,
+		.aead.decrypt		= rfc3961_aead_decrypt,
+		.aead.ivsize		= 0,
+		.aead.maxauthsize	= 0,
+		.aead.chunksize		= 16,
+
+		.aead.base.cra_flags		= CRYPTO_ALG_ALLOCATES_MEMORY,
+		.aead.base.cra_blocksize	= 1,
+		.aead.base.cra_ctxsize		= sizeof(struct krb5_ctx),
+		.aead.base.cra_alignmask	= 0,
+		.aead.base.cra_priority		= 100,
+		.aead.base.cra_name		= "krb5-aes128-cts-hmac-sha1-96",
+		.aead.base.cra_driver_name	= "krb5-aes128-cts-hmac-sha1-96-generic",
+		.aead.base.cra_module		= THIS_MODULE,
+	}, {
+		.etype			= KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96,
+		.ctype			= KRB5_CKSUMTYPE_HMAC_SHA1_96_AES256,
+		.name			= "aes256-cts-hmac-sha1-96",
+		.encrypt_name		= "cts(cbc(aes))",
+		.cksum_name		= "hmac(sha1)",
+		.hash_name		= "sha1",
+		.key_bytes		= 32,
+		.key_len		= 32,
+		.Kc_len			= 32,
+		.Ke_len			= 32,
+		.Ki_len			= 32,
+		.block_len		= 16,
+		.conf_len		= 16,
+		.cksum_len		= 12,
+		.hash_len		= 20,
+		.prf_len		= 16,
+		.keyed_cksum		= true,
+		.random_to_key		= NULL, /* Identity */
+		.profile		= &rfc3961_simplified_profile,
+
+		.aead.setkey		= krb5_setkey,
+		.aead.setauthsize	= NULL,
+		.aead.encrypt		= rfc3961_aead_encrypt,
+		.aead.decrypt		= rfc3961_aead_decrypt,
+		.aead.ivsize		= 0,
+		.aead.maxauthsize	= 0,
+		.aead.chunksize		= 16,
+
+		.aead.base.cra_flags		= CRYPTO_ALG_ALLOCATES_MEMORY,
+		.aead.base.cra_blocksize	= 1,
+		.aead.base.cra_ctxsize		= sizeof(struct krb5_ctx),
+		.aead.base.cra_alignmask	= 0,
+		.aead.base.cra_priority		= 100,
+		.aead.base.cra_name		= "krb5-aes256-cts-hmac-sha1-96",
+		.aead.base.cra_driver_name	= "krb5-aes256-cts-hmac-sha1-96-generic",
+		.aead.base.cra_module		= THIS_MODULE,
+	}, {
+		.etype			= KRB5_ENCTYPE_CAMELLIA128_CTS_CMAC,
+		.ctype			= KRB5_CKSUMTYPE_CMAC_CAMELLIA128,
+		.name			= "camellia128-cts-cmac",
+		.encrypt_name		= "cts(cbc(camellia))",
+		.cksum_name		= "cmac(camellia)",
+		.hash_name		= NULL,
+		.key_bytes		= 16,
+		.key_len		= 16,
+		.Kc_len			= 16,
+		.Ke_len			= 16,
+		.Ki_len			= 16,
+		.block_len		= 16,
+		.conf_len		= 16,
+		.cksum_len		= 16,
+		.hash_len		= 16,
+		.prf_len		= 16,
+		.keyed_cksum		= true,
+		.random_to_key		= NULL, /* Identity */
+		.profile		= &rfc6803_crypto_profile,
+
+		.aead.setkey		= krb5_setkey,
+		.aead.setauthsize	= NULL,
+		.aead.encrypt		= rfc3961_aead_encrypt,
+		.aead.decrypt		= rfc3961_aead_decrypt,
+		.aead.ivsize		= 0,
+		.aead.maxauthsize	= 0,
+		.aead.chunksize		= 16,
+
+		.aead.base.cra_flags		= CRYPTO_ALG_ALLOCATES_MEMORY,
+		.aead.base.cra_blocksize	= 1,
+		.aead.base.cra_ctxsize		= sizeof(struct krb5_ctx),
+		.aead.base.cra_alignmask	= 0,
+		.aead.base.cra_priority		= 100,
+		.aead.base.cra_name		= "krb5-camellia128-cts-cmac",
+		.aead.base.cra_driver_name	= "krb5-camellia128-cts-cmac-generic",
+		.aead.base.cra_module		= THIS_MODULE,
+	}, {
+		.etype			= KRB5_ENCTYPE_CAMELLIA256_CTS_CMAC,
+		.ctype			= KRB5_CKSUMTYPE_CMAC_CAMELLIA256,
+		.name			= "camellia256-cts-cmac",
+		.encrypt_name		= "cts(cbc(camellia))",
+		.cksum_name		= "cmac(camellia)",
+		.hash_name		= NULL,
+		.key_bytes		= 32,
+		.key_len		= 32,
+		.Kc_len			= 32,
+		.Ke_len			= 32,
+		.Ki_len			= 32,
+		.block_len		= 16,
+		.conf_len		= 16,
+		.cksum_len		= 16,
+		.hash_len		= 16,
+		.prf_len		= 16,
+		.keyed_cksum		= true,
+		.random_to_key		= NULL, /* Identity */
+		.profile		= &rfc6803_crypto_profile,
+
+		.aead.setkey		= krb5_setkey,
+		.aead.setauthsize	= NULL,
+		.aead.encrypt		= rfc3961_aead_encrypt,
+		.aead.decrypt		= rfc3961_aead_decrypt,
+		.aead.ivsize		= 0,
+		.aead.maxauthsize	= 0,
+		.aead.chunksize		= 16,
+
+		.aead.base.cra_flags		= CRYPTO_ALG_ALLOCATES_MEMORY,
+		.aead.base.cra_blocksize	= 1,
+		.aead.base.cra_ctxsize		= sizeof(struct krb5_ctx),
+		.aead.base.cra_alignmask	= 0,
+		.aead.base.cra_priority		= 100,
+		.aead.base.cra_name		= "krb5-camellia256-cts-cmac",
+		.aead.base.cra_driver_name	= "krb5-camellia256-cts-cmac-generic",
+		.aead.base.cra_module		= THIS_MODULE,
+	}, {
+		.etype			= KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128,
+		.ctype			= KRB5_CKSUMTYPE_HMAC_SHA256_128_AES128,
+		.name			= "aes128-cts-hmac-sha256-128",
+		.encrypt_name		= "cts(cbc(aes))",
+		.cksum_name		= "hmac(sha256)",
+		.hash_name		= "sha256",
+		.key_bytes		= 16,
+		.key_len		= 16,
+		.Kc_len			= 16,
+		.Ke_len			= 16,
+		.Ki_len			= 16,
+		.block_len		= 16,
+		.conf_len		= 16,
+		.cksum_len		= 16,
+		.hash_len		= 20,
+		.prf_len		= 32,
+		.keyed_cksum		= true,
+		.random_to_key		= NULL, /* Identity */
+		.profile		= &rfc8009_crypto_profile,
+
+		.aead.setkey		= krb5_setkey,
+		.aead.setauthsize	= NULL,
+		.aead.encrypt		= rfc8009_aead_encrypt,
+		.aead.decrypt		= rfc8009_aead_decrypt,
+		.aead.ivsize		= 0,
+		.aead.maxauthsize	= 0,
+		.aead.chunksize		= 16,
+
+		.aead.base.cra_flags		= CRYPTO_ALG_ALLOCATES_MEMORY,
+		.aead.base.cra_blocksize	= 1,
+		.aead.base.cra_ctxsize		= sizeof(struct krb5_ctx),
+		.aead.base.cra_alignmask	= 0,
+		.aead.base.cra_priority		= 100,
+		.aead.base.cra_name		= "krb5-aes128-cts-hmac-sha256-128",
+		.aead.base.cra_driver_name	= "krb5-aes128-cts-hmac-sha256-128generic",
+		.aead.base.cra_module		= THIS_MODULE,
+	}, {
+		.etype			= KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192,
+		.ctype			= KRB5_CKSUMTYPE_HMAC_SHA384_192_AES256,
+		.name			= "aes256-cts-hmac-sha384-192",
+		.encrypt_name		= "cts(cbc(aes))",
+		.cksum_name		= "hmac(sha384)",
+		.hash_name		= "sha384",
+		.key_bytes		= 32,
+		.key_len		= 32,
+		.Kc_len			= 24,
+		.Ke_len			= 32,
+		.Ki_len			= 24,
+		.block_len		= 16,
+		.conf_len		= 16,
+		.cksum_len		= 24,
+		.hash_len		= 20,
+		.prf_len		= 48,
+		.keyed_cksum		= true,
+		.random_to_key		= NULL, /* Identity */
+		.profile		= &rfc8009_crypto_profile,
+
+		.aead.setkey		= krb5_setkey,
+		.aead.setauthsize	= NULL,
+		.aead.encrypt		= rfc8009_aead_encrypt,
+		.aead.decrypt		= rfc8009_aead_decrypt,
+		.aead.ivsize		= 0,
+		.aead.maxauthsize	= 0,
+		.aead.chunksize		= 16,
+
+		.aead.base.cra_flags		= CRYPTO_ALG_ALLOCATES_MEMORY,
+		.aead.base.cra_blocksize	= 1,
+		.aead.base.cra_ctxsize		= sizeof(struct krb5_ctx),
+		.aead.base.cra_alignmask	= 0,
+		.aead.base.cra_priority		= 100,
+		.aead.base.cra_name		= "krb5-aes256-cts-hmac-sha384-192",
+		.aead.base.cra_driver_name	= "krb5-aes256-cts-hmac-sha384-192-generic",
+		.aead.base.cra_module		= THIS_MODULE,
+	},
+};
+
+/**
+ * crypto_krb5_find_enctype - Find the handler for a Kerberos5 encryption type
+ * @enctype: The standard Kerberos encryption type number
+ *
+ * Look up a Kerberos encryption type by number.  If successful, returns a
+ * pointer to the type description; returns NULL otherwise.
+ */
+const struct krb5_enctype *crypto_krb5_find_enctype(u32 enctype)
+{
+	const struct krb5_enctype *krb5;
+	size_t i;
+
+	for (i = 0; i < ARRAY_SIZE(krb5_enctypes); i++) {
+		krb5 = &krb5_enctypes[i];
+		if (krb5->etype == enctype)
+			return krb5;
+	}
+
+	return NULL;
+}
+EXPORT_SYMBOL(crypto_krb5_find_enctype);
+
+static int __init krb5_module_init(void)
+{
+	int i, ret, count = ARRAY_SIZE(krb5_enctypes);
+
+	for (i = 0; i < count; i++) {
+		ret = crypto_register_aead(&krb5_enctypes[i].aead);
+		if (ret)
+			goto err;
+	}
+
+	ret = krb5_selftest();
+	if (ret < 0)
+		goto err;
+	return 0;
+
+err:
+	for (--i; i >= 0; --i)
+		crypto_unregister_aead(&krb5_enctypes[i].aead);
+	return ret;
+}
+
+static void __exit krb5_module_exit(void)
+{
+	for (int i = 0; i < ARRAY_SIZE(krb5_enctypes); i++)
+		crypto_unregister_aead(&krb5_enctypes[i].aead);
+}
+
+subsys_initcall(krb5_module_init);
+module_exit(krb5_module_exit);
+
+MODULE_DESCRIPTION("Kerberos 5 crypto");
+MODULE_AUTHOR("Red Hat, Inc.");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_CRYPTO("krb5");
diff --git a/crypto/krb5/rfc3961_simplified.c b/crypto/krb5/rfc3961_simplified.c
new file mode 100644
index 000000000000..d5f0837bd424
--- /dev/null
+++ b/crypto/krb5/rfc3961_simplified.c
@@ -0,0 +1,815 @@
+// SPDX-License-Identifier: BSD-3-Clause
+/* rfc3961 Kerberos 5 simplified crypto profile.
+ *
+ * Parts borrowed from net/sunrpc/auth_gss/.
+ */
+/*
+ * COPYRIGHT (c) 2008
+ * The Regents of the University of Michigan
+ * ALL RIGHTS RESERVED
+ *
+ * Permission is granted to use, copy, create derivative works
+ * and redistribute this software and such derivative works
+ * for any purpose, so long as the name of The University of
+ * Michigan is not used in any advertising or publicity
+ * pertaining to the use of distribution of this software
+ * without specific, written prior authorization.  If the
+ * above copyright notice or any other identification of the
+ * University of Michigan is included in any copy of any
+ * portion of this software, then the disclaimer below must
+ * also be included.
+ *
+ * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION
+ * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY
+ * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF
+ * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
+ * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE
+ * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
+ * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGES.
+ */
+
+/*
+ * Copyright (C) 1998 by the FundsXpress, INC.
+ *
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may require
+ * a specific license from the United States Government.  It is the
+ * responsibility of any person or organization contemplating export to
+ * obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of FundsXpress. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  FundsXpress makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/*
+ * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells at redhat.com)
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/random.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/lcm.h>
+#include <crypto/skcipher.h>
+#include <crypto/hash.h>
+#include "internal.h"
+
+size_t sg_count(struct scatterlist *sg, int *_nents)
+{
+	size_t total = 0;
+	int nents = 0;
+
+	for (; sg; sg = sg_next(sg)) {
+		nents++;
+		total += sg->length;
+	}
+
+	*_nents = nents;
+	return total;
+}
+
+int crypto_shash_update_sg(struct shash_desc *desc, struct scatterlist *sg,
+			   size_t offset, size_t len)
+{
+	do {
+		int ret;
+
+		if (offset < sg->length) {
+			struct page *page = sg_page(sg);
+			void *p = kmap_local_page(page);
+			void *q = p + sg->offset + offset;
+			size_t seg = min_t(size_t, len, sg->length - offset);
+
+			ret = crypto_shash_update(desc, q, seg);
+			kunmap_local(p);
+			if (ret < 0)
+				return ret;
+			len -= seg;
+			offset = 0;
+		} else {
+			offset -= sg->length;
+		}
+	} while (len > 0 && (sg = sg_next(sg)));
+	return 0;
+}
+
+/* Maximum blocksize for the supported crypto algorithms */
+#define KRB5_MAX_BLOCKSIZE  (16)
+
+static int rfc3961_do_encrypt(struct crypto_sync_skcipher *tfm, void *iv,
+			      const struct krb5_buffer *in, struct krb5_buffer *out)
+{
+	struct scatterlist sg[1];
+	u8 local_iv[KRB5_MAX_BLOCKSIZE] __aligned(KRB5_MAX_BLOCKSIZE) = {0};
+	SYNC_SKCIPHER_REQUEST_ON_STACK(req, tfm);
+	int ret;
+
+	if (WARN_ON(in->len != out->len))
+		return -EINVAL;
+	if (out->len % crypto_sync_skcipher_blocksize(tfm) != 0)
+		return -EINVAL;
+
+	if (crypto_sync_skcipher_ivsize(tfm) > KRB5_MAX_BLOCKSIZE)
+		return -EINVAL;
+
+	if (iv)
+		memcpy(local_iv, iv, crypto_sync_skcipher_ivsize(tfm));
+
+	memcpy(out->data, in->data, out->len);
+	sg_init_one(sg, out->data, out->len);
+
+	skcipher_request_set_sync_tfm(req, tfm);
+	skcipher_request_set_callback(req, 0, NULL, NULL);
+	skcipher_request_set_crypt(req, sg, sg, out->len, local_iv);
+
+	ret = crypto_skcipher_encrypt(req);
+	skcipher_request_zero(req);
+	return ret;
+}
+
+/*
+ * Calculate an unkeyed basic hash.
+ */
+static int rfc3961_calc_H(const struct krb5_enctype *krb5,
+			  const struct krb5_buffer *data,
+			  struct krb5_buffer *digest,
+			  gfp_t gfp)
+{
+	struct crypto_shash *tfm;
+	struct shash_desc *desc;
+	size_t desc_size;
+	int ret = -ENOMEM;
+
+	tfm = crypto_alloc_shash(krb5->hash_name, 0, 0);
+	if (IS_ERR(tfm))
+		return (PTR_ERR(tfm) == -ENOENT) ? -ENOPKG : PTR_ERR(tfm);
+
+	desc_size = crypto_shash_descsize(tfm) + sizeof(*desc);
+
+	desc = kzalloc(desc_size, gfp);
+	if (!desc)
+		goto error_tfm;
+
+	digest->len = crypto_shash_digestsize(tfm);
+	digest->data = kzalloc(digest->len, gfp);
+	if (!digest->data)
+		goto error_desc;
+
+	desc->tfm = tfm;
+	ret = crypto_shash_init(desc);
+	if (ret < 0)
+		goto error_digest;
+
+	ret = crypto_shash_finup(desc, data->data, data->len, digest->data);
+	if (ret < 0)
+		goto error_digest;
+
+	goto error_desc;
+
+error_digest:
+	kfree_sensitive(digest->data);
+error_desc:
+	kfree_sensitive(desc);
+error_tfm:
+	crypto_free_shash(tfm);
+	return ret;
+}
+
+/*
+ * This is the n-fold function as described in rfc3961, sec 5.1
+ * Taken from MIT Kerberos and modified.
+ */
+static void rfc3961_nfold(const struct krb5_buffer *source, struct krb5_buffer *result)
+{
+	const u8 *in = source->data;
+	u8 *out = result->data;
+	unsigned long ulcm;
+	unsigned int inbits, outbits;
+	int byte, i, msbit;
+
+	/* the code below is more readable if I make these bytes instead of bits */
+	inbits = source->len;
+	outbits = result->len;
+
+	/* first compute lcm(n,k) */
+	ulcm = lcm(inbits, outbits);
+
+	/* now do the real work */
+	memset(out, 0, outbits);
+	byte = 0;
+
+	/* this will end up cycling through k lcm(k,n)/k times, which
+	 * is correct.
+	 */
+	for (i = ulcm-1; i >= 0; i--) {
+		/* compute the msbit in k which gets added into this byte */
+		msbit = (
+			/* first, start with the msbit in the first,
+			 * unrotated byte
+			 */
+			((inbits << 3) - 1) +
+			/* then, for each byte, shift to the right
+			 * for each repetition
+			 */
+			(((inbits << 3) + 13) * (i/inbits)) +
+			/* last, pick out the correct byte within
+			 * that shifted repetition
+			 */
+			((inbits - (i % inbits)) << 3)
+			 ) % (inbits << 3);
+
+		/* pull out the byte value itself */
+		byte += (((in[((inbits - 1) - (msbit >> 3)) % inbits] << 8) |
+			  (in[((inbits)     - (msbit >> 3)) % inbits]))
+			 >> ((msbit & 7) + 1)) & 0xff;
+
+		/* do the addition */
+		byte += out[i % outbits];
+		out[i % outbits] = byte & 0xff;
+
+		/* keep around the carry bit, if any */
+		byte >>= 8;
+	}
+
+	/* if there's a carry bit left over, add it back in */
+	if (byte) {
+		for (i = outbits - 1; i >= 0; i--) {
+			/* do the addition */
+			byte += out[i];
+			out[i] = byte & 0xff;
+
+			/* keep around the carry bit, if any */
+			byte >>= 8;
+		}
+	}
+}
+
+/*
+ * Calculate a derived key, DK(Base Key, Well-Known Constant)
+ *
+ * DK(Key, Constant) = random-to-key(DR(Key, Constant))
+ * DR(Key, Constant) = k-truncate(E(Key, Constant, initial-cipher-state))
+ * K1 = E(Key, n-fold(Constant), initial-cipher-state)
+ * K2 = E(Key, K1, initial-cipher-state)
+ * K3 = E(Key, K2, initial-cipher-state)
+ * K4 = ...
+ * DR(Key, Constant) = k-truncate(K1 | K2 | K3 | K4 ...)
+ * [rfc3961 sec 5.1]
+ */
+static int rfc3961_calc_DK(const struct krb5_enctype *krb5,
+			   const struct krb5_buffer *inkey,
+			   const struct krb5_buffer *in_constant,
+			   struct krb5_buffer *result,
+			   gfp_t gfp)
+{
+	unsigned int blocksize, keybytes, keylength, n;
+	struct krb5_buffer inblock, outblock, rawkey;
+	struct crypto_sync_skcipher *cipher;
+	int ret = -EINVAL;
+
+	blocksize = krb5->block_len;
+	keybytes = krb5->key_bytes;
+	keylength = krb5->key_len;
+
+	if (inkey->len != keylength || result->len != keylength)
+		return -EINVAL;
+	if (!krb5->random_to_key && result->len != keybytes)
+		ret = -EINVAL;
+
+	cipher = crypto_alloc_sync_skcipher(krb5->encrypt_name, 0, 0);
+	if (IS_ERR(cipher)) {
+		ret = (PTR_ERR(cipher) == -ENOENT) ? -ENOPKG : PTR_ERR(cipher);
+		goto err_return;
+	}
+	ret = crypto_sync_skcipher_setkey(cipher, inkey->data, inkey->len);
+	if (ret < 0)
+		goto err_free_cipher;
+
+	ret = -ENOMEM;
+	inblock.data = kzalloc(blocksize * 2 + keybytes, gfp);
+	if (!inblock.data)
+		goto err_free_cipher;
+
+	inblock.len	= blocksize;
+	outblock.data	= inblock.data + blocksize;
+	outblock.len	= blocksize;
+	rawkey.data	= outblock.data + blocksize;
+	rawkey.len	= keybytes;
+
+	/* initialize the input block */
+
+	if (in_constant->len == inblock.len)
+		memcpy(inblock.data, in_constant->data, inblock.len);
+	else
+		rfc3961_nfold(in_constant, &inblock);
+
+	/* loop encrypting the blocks until enough key bytes are generated */
+	n = 0;
+	while (n < rawkey.len) {
+		rfc3961_do_encrypt(cipher, NULL, &inblock, &outblock);
+
+		if (keybytes - n <= outblock.len) {
+			memcpy(rawkey.data + n, outblock.data, keybytes - n);
+			break;
+		}
+
+		memcpy(rawkey.data + n, outblock.data, outblock.len);
+		memcpy(inblock.data, outblock.data, outblock.len);
+		n += outblock.len;
+	}
+
+	/* postprocess the key */
+	if (!krb5->random_to_key) {
+		/* Identity random-to-key function. */
+		memcpy(result->data, rawkey.data, rawkey.len);
+		ret = 0;
+	} else {
+		ret = krb5->random_to_key(krb5, &rawkey, result);
+	}
+
+	kfree_sensitive(inblock.data);
+err_free_cipher:
+	crypto_free_sync_skcipher(cipher);
+err_return:
+	return ret;
+}
+
+/*
+ * Calculate single encryption, E()
+ *
+ *	E(Key, octets)
+ */
+static int rfc3961_calc_E(const struct krb5_enctype *krb5,
+			  const struct krb5_buffer *key,
+			  const struct krb5_buffer *in_data,
+			  struct krb5_buffer *result,
+			  gfp_t gfp)
+{
+	struct crypto_sync_skcipher *cipher;
+	int ret;
+
+	cipher = crypto_alloc_sync_skcipher(krb5->encrypt_name, 0, 0);
+	if (IS_ERR(cipher)) {
+		ret = (PTR_ERR(cipher) == -ENOENT) ? -ENOPKG : PTR_ERR(cipher);
+		goto err;
+	}
+
+	ret = crypto_sync_skcipher_setkey(cipher, key->data, key->len);
+	if (ret < 0)
+		goto err_free;
+
+	ret = rfc3961_do_encrypt(cipher, NULL, in_data, result);
+
+err_free:
+	crypto_free_sync_skcipher(cipher);
+err:
+	return ret;
+}
+
+/*
+ * Calculate the pseudo-random function, PRF().
+ *
+ *      tmp1 = H(octet-string)
+ *      tmp2 = truncate tmp1 to multiple of m
+ *      PRF = E(DK(protocol-key, prfconstant), tmp2, initial-cipher-state)
+ *
+ *      The "prfconstant" used in the PRF operation is the three-octet string
+ *      "prf".
+ *      [rfc3961 sec 5.3]
+ */
+static int rfc3961_calc_PRF(const struct krb5_enctype *krb5,
+			    const struct krb5_buffer *protocol_key,
+			    const struct krb5_buffer *octet_string,
+			    struct krb5_buffer *result,
+			    gfp_t gfp)
+{
+	static const struct krb5_buffer prfconstant = { 3, "prf" };
+	struct krb5_buffer derived_key;
+	struct krb5_buffer tmp1, tmp2;
+	unsigned int m = krb5->block_len;
+	void *buffer;
+	int ret;
+
+	if (result->len != krb5->prf_len)
+		return -EINVAL;
+
+	tmp1.len = krb5->hash_len;
+	derived_key.len = krb5->key_bytes;
+	buffer = kzalloc(round16(tmp1.len) + round16(derived_key.len), gfp);
+	if (!buffer)
+		return -ENOMEM;
+
+	tmp1.data = buffer;
+	derived_key.data = buffer + round16(tmp1.len);
+
+	ret = rfc3961_calc_H(krb5, octet_string, &tmp1, gfp);
+	if (ret < 0)
+		goto err;
+
+	tmp2.len = tmp1.len & ~(m - 1);
+	tmp2.data = tmp1.data;
+
+	ret = rfc3961_calc_DK(krb5, protocol_key, &prfconstant, &derived_key, gfp);
+	if (ret < 0)
+		goto err;
+
+	ret = rfc3961_calc_E(krb5, &derived_key, &tmp2, result, gfp);
+
+err:
+	kfree_sensitive(buffer);
+	return ret;
+}
+
+/*
+ * Apply encryption and checksumming functions to part of a message.  The
+ * caller is responsible for laying out the message and inserting a confounder.
+ *
+ * req->cryptlen indicates the size of the area in the source to be encrypted,
+ * and must include any metadata prior to the data area, such as the
+ * confounder.  Space for post-data metadata, such as the checksum, only needs
+ * to exist in the destination.
+ */
+static int rfc3961_encrypt(struct aead_request *req)
+{
+	struct skcipher_request	*ci;
+	struct crypto_aead *tfm = crypto_aead_reqtfm(req);
+	struct krb5_enctype *krb5 = crypto_krb5_enctype(tfm);
+	struct scatterlist *src = req->src, *dst = req->dst;
+	struct shash_desc *desc;
+	struct krb5_ctx *ctx = crypto_krb5_ctx(tfm);
+	size_t bsize, enc_len = req->cryptlen;
+	size_t src_len, dst_len, done;
+	void *buffer;
+	int ret, nr_src, nr_dst;
+	u8 *cksum, *iv;
+
+	src_len = sg_count(src, &nr_src);
+	dst_len = sg_count(dst, &nr_dst);
+
+	if (WARN_ON(enc_len < krb5->conf_len) ||
+	    WARN_ON(src_len < enc_len) ||
+	    WARN_ON(dst_len < enc_len + krb5->cksum_len))
+		return -EINVAL;
+
+	/* The message is laid out thusly:
+	 *
+	 *	Confounder||Data||Padding||Integrity
+	 *
+	 * The Padding may or may not be present, but the Integrity checksum
+	 * must be right at the end of the message so that we can find it.  The
+	 * Confounder, Data and Padding are encrypted; the Integrity checksum
+	 * is not.  The Integrity checksum is over the plaintext.
+	 */
+	enc_len	= req->cryptlen;
+
+	bsize = krb5_shash_size(ctx->Ki) +
+		krb5_digest_size(ctx->Ki) +
+		krb5_sync_skcipher_size(ctx->Ke) +
+		krb5_sync_skcipher_ivsize(ctx->Ke);
+	bsize = umax(umax(bsize, krb5->conf_len), krb5->block_len);
+	buffer = kzalloc(bsize, GFP_NOFS);
+	if (!buffer)
+		return -ENOMEM;
+
+	/* Calculate the checksum using key Ki */
+	cksum = buffer + krb5_shash_size(ctx->Ki);
+
+	desc = buffer;
+	desc->tfm = ctx->Ki;
+	ret = crypto_shash_init(desc);
+	if (ret < 0)
+		goto error;
+	ret = crypto_shash_update_sg(desc, src, 0, enc_len);
+	if (ret < 0)
+		goto error;
+	ret = crypto_shash_final(desc, cksum);
+	if (ret < 0)
+		goto error;
+
+	/* Append the checksum into the buffer. */
+	ret = -EFAULT;
+	done = sg_pcopy_from_buffer(dst, nr_dst, cksum, krb5->cksum_len, enc_len);
+	if (done != krb5->cksum_len)
+		goto error;
+
+	/* Encrypt the secure region with key Ke. */
+	ci = buffer +
+		krb5_shash_size(ctx->Ki) +
+		krb5_digest_size(ctx->Ki);
+	iv = buffer +
+		krb5_shash_size(ctx->Ki) +
+		krb5_digest_size(ctx->Ki) +
+		krb5_sync_skcipher_size(ctx->Ke);
+
+	skcipher_request_set_sync_tfm(ci, ctx->Ke);
+	skcipher_request_set_callback(ci, 0, NULL, NULL);
+	skcipher_request_set_crypt(ci, src, dst, enc_len, iv);
+	ret = crypto_skcipher_encrypt(ci);
+	if (ret < 0)
+		goto error;
+
+	ret = 0;
+error:
+	kfree_sensitive(buffer);
+	return ret;
+}
+
+/*
+ * Apply decryption and checksumming functions to part of an skbuff.  The
+ * offset and length are updated to reflect the actual content of the encrypted
+ * region.
+ *
+ * The associated data must contain a krb5_assoc_data struct.  At the
+ * conclusion, the output associated data is updated with the size of the
+ * encrypted data.  The associated data must be in its own scatterlist element
+ * in both chains so that we can skip over it.
+ */
+static int rfc3961_decrypt(struct aead_request *req)
+{
+	struct skcipher_request	*ci;
+	struct crypto_aead *tfm = crypto_aead_reqtfm(req);
+	struct krb5_enctype *krb5 = crypto_krb5_enctype(tfm);
+	struct scatterlist *src = req->src, *dst = req->dst;
+	struct shash_desc *desc;
+	struct krb5_ctx *ctx = crypto_krb5_ctx(tfm);
+	size_t bsize, src_len, dst_len, enc_len, msg_len = req->cryptlen, done;
+	void *buffer;
+	int ret, nr_src, nr_dst;
+	u8 *cksum, *cksum2, *iv;
+
+	src_len = sg_count(src, &nr_src);
+	dst_len = sg_count(dst, &nr_dst);
+
+	if (WARN_ON(msg_len < krb5->conf_len + krb5->cksum_len) ||
+	    WARN_ON(src_len < msg_len) ||
+	    WARN_ON(dst_len < msg_len - krb5->cksum_len))
+		return -EINVAL;
+
+	/* The integrity checksum is right up against the end. */
+	enc_len = msg_len - krb5->cksum_len;
+
+	bsize = krb5_shash_size(ctx->Ki) +
+		krb5_digest_size(ctx->Ki) * 2 +
+		krb5_sync_skcipher_size(ctx->Ke) +
+		krb5_sync_skcipher_ivsize(ctx->Ke);
+	buffer = kzalloc(bsize, GFP_NOFS);
+	if (!buffer)
+		return -ENOMEM;
+
+	cksum = buffer +
+		krb5_shash_size(ctx->Ki);
+	cksum2 = buffer +
+		krb5_shash_size(ctx->Ki) +
+		krb5_digest_size(ctx->Ki);
+	ci = buffer +
+		krb5_shash_size(ctx->Ki) +
+		krb5_digest_size(ctx->Ki) * 2;
+	iv = buffer +
+		krb5_shash_size(ctx->Ki) +
+		krb5_digest_size(ctx->Ki) * 2 +
+		krb5_sync_skcipher_size(ctx->Ke);
+
+	/* Decrypt the secure region with key Ke. */
+	skcipher_request_set_sync_tfm(ci, ctx->Ke);
+	skcipher_request_set_callback(ci, 0, NULL, NULL);
+	skcipher_request_set_crypt(ci, src, dst, enc_len, iv);
+	ret = crypto_skcipher_decrypt(ci);
+	if (ret < 0)
+		goto error;
+
+	/* Calculate the checksum using key Ki */
+	desc = buffer;
+	desc->tfm = ctx->Ki;
+	ret = crypto_shash_init(desc);
+	if (ret < 0)
+		goto error;
+
+	ret = crypto_shash_update_sg(desc, src, 0, enc_len);
+	if (ret < 0)
+		goto error;
+
+	ret = crypto_shash_final(desc, cksum);
+	if (ret < 0)
+		goto error;
+
+	/* Get the checksum from the buffer. */
+	ret = -EFAULT;
+	done = sg_pcopy_to_buffer(src, nr_src, cksum2, krb5->cksum_len, enc_len);
+	if (done != krb5->cksum_len)
+		goto error;
+
+	ret = -EPROTO;
+	if (memcmp(cksum, cksum2, krb5->cksum_len) != 0)
+		goto error;
+
+	ret = 0;
+error:
+	kfree_sensitive(buffer);
+	return ret;
+}
+
+/*
+ * Generate a checksum over some metadata and part of a message and insert the
+ * MIC into the message immediately prior to the data.
+ *
+ * Any metadata to be added to the hash must be in assoc data.
+ */
+int rfc3961_get_mic(struct aead_request *req)
+{
+	struct crypto_aead *tfm = crypto_aead_reqtfm(req);
+	struct krb5_enctype *krb5 = crypto_krb5_enctype(tfm);
+	struct scatterlist *src = req->src, *dst = req->dst;
+	struct shash_desc *desc;
+	struct krb5_ctx *ctx = crypto_krb5_ctx(tfm);
+	size_t meta_len = req->assoclen, msg_len = req->cryptlen;
+	size_t src_len, dst_len, data_len, bsize, done;
+	void *buffer, *digest;
+	int ret, nr_src, nr_dst;
+
+	src_len = sg_count(src, &nr_src);
+	dst_len = sg_count(dst, &nr_dst);
+
+	if (WARN_ON(msg_len < krb5->cksum_len) ||
+	    WARN_ON(src_len < msg_len) ||
+	    WARN_ON(dst_len < src_len))
+		return -EINVAL;
+
+	/* The message is laid out thusly:
+	 *
+	 *	Checksum||Data
+	 *
+	 * The Checksum must be right at the beginning of the message so that
+	 * we can find it.
+	 */
+	data_len = msg_len - krb5->cksum_len;
+
+	bsize = krb5_shash_size(ctx->Kc) +
+		krb5_digest_size(ctx->Kc);
+	buffer = kzalloc(bsize, GFP_NOFS);
+	if (!buffer)
+		return -ENOMEM;
+
+	/* Calculate the MIC with key Kc and store it into the skb */
+	desc = buffer;
+	desc->tfm = ctx->Kc;
+	ret = crypto_shash_init(desc);
+	if (ret < 0)
+		goto error;
+
+	if (meta_len) {
+		ret = crypto_shash_update_sg(desc, src, 0, meta_len);
+		if (ret < 0)
+			goto error;
+	}
+
+	ret = crypto_shash_update_sg(desc, src, meta_len + krb5->cksum_len, data_len);
+	if (ret < 0)
+		goto error;
+
+	digest = buffer + krb5_shash_size(ctx->Kc);
+	ret = crypto_shash_final(desc, digest);
+	if (ret < 0)
+		goto error;
+
+	ret = -EFAULT;
+	done = sg_pcopy_from_buffer(dst, nr_dst, digest, krb5->cksum_len, meta_len);
+	if (done != krb5->cksum_len)
+		goto error;
+
+	ret = 0;
+error:
+	kfree_sensitive(buffer);
+	return ret;
+}
+
+/*
+ * Check the MIC on a message.
+ *
+ * Any metadata to be added to the hash must be in assoc data.
+ *
+ * [!] NOTE: This produces nothing in the destination buffer.
+ */
+int rfc3961_verify_mic(struct aead_request *req)
+{
+	struct crypto_aead *tfm = crypto_aead_reqtfm(req);
+	struct krb5_enctype *krb5 = crypto_krb5_enctype(tfm);
+	struct scatterlist *src = req->src, *dst = req->dst;
+	struct shash_desc *desc;
+	struct krb5_ctx *ctx = crypto_krb5_ctx(tfm);
+	size_t meta_len = req->assoclen, msg_len = req->cryptlen;
+	size_t src_len, dst_len, data_len, bsize, done;
+	void *buffer, *cksum, *cksum2;
+	int ret, nr_src, nr_dst;
+
+	src_len = sg_count(src, &nr_src);
+	dst_len = sg_count(dst, &nr_dst);
+
+	if (WARN_ON(msg_len < krb5->cksum_len) ||
+	    WARN_ON(src_len < msg_len) ||
+	    WARN_ON(dst_len < src_len))
+		return -EINVAL;
+
+	/* The message is laid out thusly:
+	 *
+	 *	Checksum||Data
+	 *
+	 * The Checksum must be right at the beginning of the message so that
+	 * we can find it.
+	 */
+	data_len = msg_len - krb5->cksum_len;
+
+	bsize = krb5_shash_size(ctx->Kc) +
+		krb5_digest_size(ctx->Kc) * 2;
+	buffer = kzalloc(bsize, GFP_NOFS);
+	if (!buffer)
+		return -ENOMEM;
+
+	cksum = buffer +
+		krb5_shash_size(ctx->Kc);
+	cksum2 = buffer +
+		krb5_shash_size(ctx->Kc) +
+		krb5_digest_size(ctx->Kc);
+
+	/* Calculate the MIC */
+	desc = buffer;
+	desc->tfm = ctx->Kc;
+	ret = crypto_shash_init(desc);
+	if (ret < 0)
+		goto error;
+
+	if (meta_len) {
+		ret = crypto_shash_update_sg(desc, src, 0, meta_len);
+		if (ret < 0)
+			goto error;
+	}
+
+	ret = crypto_shash_update_sg(desc, src, meta_len + krb5->cksum_len, data_len);
+	if (ret < 0)
+		goto error;
+
+	ret = crypto_shash_final(desc, cksum);
+	if (ret < 0)
+		goto error;
+
+	ret = -EFAULT;
+	done = sg_pcopy_to_buffer(src, nr_src, cksum2, krb5->cksum_len, meta_len);
+	if (done != krb5->cksum_len)
+		goto error;
+
+	if (memcmp(cksum, cksum2, krb5->cksum_len) != 0) {
+		ret = -EPROTO;
+		goto error;
+	}
+
+	ret = 0;
+error:
+	kfree_sensitive(buffer);
+	return ret;
+}
+
+int rfc3961_aead_encrypt(struct aead_request *req)
+{
+	struct crypto_aead *tfm = crypto_aead_reqtfm(req);
+	struct krb5_ctx *ctx = crypto_krb5_ctx(tfm);
+
+	if (ctx->Ke)
+		return rfc3961_encrypt(req);
+	return rfc3961_get_mic(req);
+}
+
+int rfc3961_aead_decrypt(struct aead_request *req)
+{
+	struct crypto_aead *tfm = crypto_aead_reqtfm(req);
+	struct krb5_ctx *ctx = crypto_krb5_ctx(tfm);
+
+	if (ctx->Ke)
+		return rfc3961_decrypt(req);
+	return rfc3961_verify_mic(req);
+}
+
+const struct krb5_crypto_profile rfc3961_simplified_profile = {
+	.calc_PRF	= rfc3961_calc_PRF,
+	.calc_Kc	= rfc3961_calc_DK,
+	.calc_Ke	= rfc3961_calc_DK,
+	.calc_Ki	= rfc3961_calc_DK,
+};
diff --git a/crypto/krb5/rfc6803_camellia.c b/crypto/krb5/rfc6803_camellia.c
new file mode 100644
index 000000000000..206224ab05ec
--- /dev/null
+++ b/crypto/krb5/rfc6803_camellia.c
@@ -0,0 +1,190 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* rfc6803 Camellia Encryption for Kerberos 5
+ *
+ * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells at redhat.com)
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/slab.h>
+#include "internal.h"
+
+/*
+ * Calculate the key derivation function KDF-FEEDBACK_CMAC(key, constant)
+ *
+ *	n = ceiling(k / 128)
+ *	K(0) = zeros
+ *	K(i) = CMAC(key, K(i-1) | i | constant | 0x00 | k)
+ *	DR(key, constant) = k-truncate(K(1) | K(2) | ... | K(n))
+ *	KDF-FEEDBACK-CMAC(key, constant) = random-to-key(DR(key, constant))
+ *
+ *	[rfc6803 sec 3]
+ */
+static int rfc6803_calc_KDF_FEEDBACK_CMAC(const struct krb5_enctype *krb5,
+					  const struct krb5_buffer *key,
+					  const struct krb5_buffer *constant,
+					  struct krb5_buffer *result,
+					  gfp_t gfp)
+{
+	struct crypto_shash *shash;
+	struct krb5_buffer K, data;
+	struct shash_desc *desc;
+	__be32 tmp;
+	size_t bsize, offset, seg;
+	void *buffer;
+	u32 i = 0, k = result->len * 8;
+	u8 *p;
+	int ret = -ENOMEM;
+
+	shash = crypto_alloc_shash(krb5->cksum_name, 0, 0);
+	if (IS_ERR(shash))
+		return (PTR_ERR(shash) == -ENOENT) ? -ENOPKG : PTR_ERR(shash);
+	ret = crypto_shash_setkey(shash, key->data, key->len);
+	if (ret < 0) {
+		pr_err("setkey %s failed %d %u\n", krb5->cksum_name, ret, key->len);
+		goto error_shash;
+	}
+
+	ret = -ENOMEM;
+	K.len = crypto_shash_digestsize(shash);
+	data.len = K.len + 4 + constant->len + 1 + 4;
+	bsize = krb5_shash_size(shash) +
+		krb5_digest_size(shash) +
+		crypto_roundup(K.len) +
+		crypto_roundup(data.len);
+	buffer = kzalloc(bsize, GFP_NOFS);
+	if (!buffer)
+		goto error_shash;
+
+	desc = buffer;
+	desc->tfm = shash;
+
+	K.data = buffer +
+		krb5_shash_size(shash) +
+		krb5_digest_size(shash);
+	data.data = buffer +
+		krb5_shash_size(shash) +
+		krb5_digest_size(shash) +
+		crypto_roundup(K.len);
+
+	p = data.data + K.len + 4;
+	memcpy(p, constant->data, constant->len);
+	p += constant->len;
+	*p++ = 0x00;
+	tmp = htonl(k);
+	memcpy(p, &tmp, 4);
+	p += 4;
+
+	ret = -EINVAL;
+	if (WARN_ON(p - (u8 *)data.data != data.len)) {
+		pr_err("len check\n");
+		goto error;
+	}
+
+	offset = 0;
+	do {
+		i++;
+		p = data.data;
+		memcpy(p, K.data, K.len);
+		p += K.len;
+		*(__be32 *)p = htonl(i);
+
+		ret = crypto_shash_init(desc);
+		if (ret < 0) {
+			pr_err("shash_init\n");
+			goto error;
+		}
+		ret = crypto_shash_finup(desc, data.data, data.len, K.data);
+		if (ret < 0) {
+			pr_err("shash_finup\n");
+			goto error;
+		}
+
+		seg = min_t(size_t, result->len - offset, K.len);
+		memcpy(result->data + offset, K.data, seg);
+		offset += seg;
+	} while (offset < result->len);
+
+error:
+	kfree_sensitive(buffer);
+error_shash:
+	crypto_free_shash(shash);
+	return ret;
+}
+
+/*
+ * Calculate the pseudo-random function, PRF().
+ *
+ *	Kp = KDF-FEEDBACK-CMAC(protocol-key, "prf")
+ *	PRF = CMAC(Kp, octet-string)
+ *      [rfc6803 sec 6]
+ */
+static int rfc6803_calc_PRF(const struct krb5_enctype *krb5,
+			    const struct krb5_buffer *protocol_key,
+			    const struct krb5_buffer *octet_string,
+			    struct krb5_buffer *result,
+			    gfp_t gfp)
+{
+	static const struct krb5_buffer prfconstant = { 3, "prf" };
+	struct crypto_shash *shash;
+	struct krb5_buffer Kp;
+	struct shash_desc *desc;
+	size_t bsize;
+	void *buffer;
+	int ret;
+
+	Kp.len = krb5->prf_len;
+
+	shash = crypto_alloc_shash(krb5->cksum_name, 0, 0);
+	if (IS_ERR(shash))
+		return (PTR_ERR(shash) == -ENOENT) ? -ENOPKG : PTR_ERR(shash);
+
+	ret = -EINVAL;
+	if (result->len != crypto_shash_digestsize(shash))
+		goto out_shash;
+
+	ret = -ENOMEM;
+	bsize = krb5_shash_size(shash) +
+		krb5_digest_size(shash) +
+		crypto_roundup(Kp.len);
+	buffer = kzalloc(bsize, GFP_NOFS);
+	if (!buffer)
+		goto out_shash;
+
+	Kp.data = buffer +
+		krb5_shash_size(shash) +
+		krb5_digest_size(shash);
+
+	ret = rfc6803_calc_KDF_FEEDBACK_CMAC(krb5, protocol_key, &prfconstant,
+					     &Kp, gfp);
+	if (ret < 0)
+		goto out;
+
+	ret = crypto_shash_setkey(shash, Kp.data, Kp.len);
+	if (ret < 0)
+		goto out;
+
+	desc = buffer;
+	desc->tfm = shash;
+	ret = crypto_shash_init(desc);
+	if (ret < 0)
+		goto out;
+
+	ret = crypto_shash_finup(desc, octet_string->data, octet_string->len, result->data);
+	if (ret < 0)
+		goto out;
+
+out:
+	kfree_sensitive(buffer);
+out_shash:
+	crypto_free_shash(shash);
+	return ret;
+}
+
+const struct krb5_crypto_profile rfc6803_crypto_profile = {
+	.calc_PRF	= rfc6803_calc_PRF,
+	.calc_Kc	= rfc6803_calc_KDF_FEEDBACK_CMAC,
+	.calc_Ke	= rfc6803_calc_KDF_FEEDBACK_CMAC,
+	.calc_Ki	= rfc6803_calc_KDF_FEEDBACK_CMAC,
+};
diff --git a/crypto/krb5/rfc8009_aes2.c b/crypto/krb5/rfc8009_aes2.c
new file mode 100644
index 000000000000..60b43479e108
--- /dev/null
+++ b/crypto/krb5/rfc8009_aes2.c
@@ -0,0 +1,394 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* rfc8009 AES Encryption with HMAC-SHA2 for Kerberos 5
+ *
+ * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells at redhat.com)
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/slab.h>
+#include <linux/random.h>
+#include "internal.h"
+
+static const struct krb5_buffer rfc8009_no_context = { .len = 0, .data = "" };
+
+/*
+ * Calculate the key derivation function KDF-HMAC-SHA2(key, label, [context,] k)
+ *
+ *	KDF-HMAC-SHA2(key, label, [context,] k) = k-truncate(K1)
+ *
+ *	Using the appropriate one of:
+ *		K1 = HMAC-SHA-256(key, 0x00000001 | label | 0x00 | k)
+ *		K1 = HMAC-SHA-384(key, 0x00000001 | label | 0x00 | k)
+ *		K1 = HMAC-SHA-256(key, 0x00000001 | label | 0x00 | context | k)
+ *		K1 = HMAC-SHA-384(key, 0x00000001 | label | 0x00 | context | k)
+ *	[rfc8009 sec 3]
+ */
+static int rfc8009_calc_KDF_HMAC_SHA2(const struct krb5_enctype *krb5,
+				      const struct krb5_buffer *key,
+				      const struct krb5_buffer *label,
+				      const struct krb5_buffer *context,
+				      unsigned int k,
+				      struct krb5_buffer *result,
+				      gfp_t gfp)
+{
+	struct crypto_shash *shash;
+	struct krb5_buffer K1, data;
+	struct shash_desc *desc;
+	__be32 tmp;
+	size_t bsize;
+	void *buffer;
+	u8 *p;
+	int ret = -ENOMEM;
+
+	if (WARN_ON(result->len != k / 8))
+		return -EINVAL;
+
+	shash = crypto_alloc_shash(krb5->cksum_name, 0, 0);
+	if (IS_ERR(shash))
+		return (PTR_ERR(shash) == -ENOENT) ? -ENOPKG : PTR_ERR(shash);
+	ret = crypto_shash_setkey(shash, key->data, key->len);
+	if (ret < 0)
+		goto error_shash;
+
+	ret = -EINVAL;
+	if (WARN_ON(crypto_shash_digestsize(shash) * 8 < k))
+		goto error_shash;
+
+	ret = -ENOMEM;
+	data.len = 4 + label->len + 1 + context->len + 4;
+	bsize = krb5_shash_size(shash) +
+		krb5_digest_size(shash) +
+		crypto_roundup(data.len);
+	buffer = kzalloc(bsize, GFP_NOFS);
+	if (!buffer)
+		goto error_shash;
+
+	desc = buffer;
+	desc->tfm = shash;
+	ret = crypto_shash_init(desc);
+	if (ret < 0)
+		goto error;
+
+	p = data.data = buffer +
+		krb5_shash_size(shash) +
+		krb5_digest_size(shash);
+	*(__be32 *)p = htonl(0x00000001);
+	p += 4;
+	memcpy(p, label->data, label->len);
+	p += label->len;
+	*p++ = 0;
+	memcpy(p, context->data, context->len);
+	p += context->len;
+	tmp = htonl(k);
+	memcpy(p, &tmp, 4);
+	p += 4;
+
+	ret = -EINVAL;
+	if (WARN_ON(p - (u8 *)data.data != data.len))
+		goto error;
+
+	K1.len = crypto_shash_digestsize(shash);
+	K1.data = buffer +
+		krb5_shash_size(shash);
+
+	ret = crypto_shash_finup(desc, data.data, data.len, K1.data);
+	if (ret < 0)
+		goto error;
+
+	memcpy(result->data, K1.data, result->len);
+
+error:
+	kfree_sensitive(buffer);
+error_shash:
+	crypto_free_shash(shash);
+	return ret;
+}
+
+/*
+ * Calculate the pseudo-random function, PRF().
+ *
+ *	PRF = KDF-HMAC-SHA2(input-key, "prf", octet-string, 256)
+ *	PRF = KDF-HMAC-SHA2(input-key, "prf", octet-string, 384)
+ *
+ *      The "prfconstant" used in the PRF operation is the three-octet string
+ *      "prf".
+ *      [rfc8009 sec 5]
+ */
+static int rfc8009_calc_PRF(const struct krb5_enctype *krb5,
+			    const struct krb5_buffer *input_key,
+			    const struct krb5_buffer *octet_string,
+			    struct krb5_buffer *result,
+			    gfp_t gfp)
+{
+	static const struct krb5_buffer prfconstant = { 3, "prf" };
+
+	return rfc8009_calc_KDF_HMAC_SHA2(krb5, input_key, &prfconstant,
+					  octet_string, krb5->prf_len * 8,
+					  result, gfp);
+}
+
+/*
+ * Derive Ke.
+ *	Ke = KDF-HMAC-SHA2(base-key, usage | 0xAA, 128)
+ *	Ke = KDF-HMAC-SHA2(base-key, usage | 0xAA, 256)
+ *      [rfc8009 sec 5]
+ */
+static int rfc8009_calc_Ke(const struct krb5_enctype *krb5,
+			   const struct krb5_buffer *base_key,
+			   const struct krb5_buffer *usage_constant,
+			   struct krb5_buffer *result,
+			   gfp_t gfp)
+{
+	return rfc8009_calc_KDF_HMAC_SHA2(krb5, base_key, usage_constant,
+					  &rfc8009_no_context, krb5->key_bytes * 8,
+					  result, gfp);
+}
+
+/*
+ * Derive Kc/Ki
+ *	Kc = KDF-HMAC-SHA2(base-key, usage | 0x99, 128)
+ *	Ki = KDF-HMAC-SHA2(base-key, usage | 0x55, 128)
+ *	Kc = KDF-HMAC-SHA2(base-key, usage | 0x99, 192)
+ *	Ki = KDF-HMAC-SHA2(base-key, usage | 0x55, 192)
+ *      [rfc8009 sec 5]
+ */
+static int rfc8009_calc_Ki(const struct krb5_enctype *krb5,
+			   const struct krb5_buffer *base_key,
+			   const struct krb5_buffer *usage_constant,
+			   struct krb5_buffer *result,
+			   gfp_t gfp)
+{
+	return rfc8009_calc_KDF_HMAC_SHA2(krb5, base_key, usage_constant,
+					  &rfc8009_no_context, krb5->cksum_len * 8,
+					  result, gfp);
+}
+
+/*
+ * Apply encryption and checksumming functions to part of a message.  The
+ * caller is responsible for laying out the message and inserting a confounder.
+ *
+ * req->cryptlen indicates the size of the area in the source to be encrypted,
+ * and must include any metadata prior to the data area, such as the
+ * confounder.  Space for post-data metadata, such as the checksum, only needs
+ * to exist in the destination.
+ */
+static int rfc8009_encrypt(struct aead_request *req)
+{
+	struct skcipher_request	*ci;
+	struct crypto_aead *tfm = crypto_aead_reqtfm(req);
+	struct krb5_enctype *krb5 = crypto_krb5_enctype(tfm);
+	struct scatterlist *src = req->src, *dst = req->dst;
+	struct shash_desc *desc;
+	struct krb5_ctx *ctx = crypto_krb5_ctx(tfm);
+	size_t bsize, enc_len = req->cryptlen, msg_len;
+	size_t src_len, dst_len, done;
+	void *buffer;
+	int ret, nr_src, nr_dst;
+	u8 *cksum, *iv;
+
+	src_len = sg_count(src, &nr_src);
+	dst_len = sg_count(dst, &nr_dst);
+
+	if (WARN_ON(enc_len < krb5->conf_len) ||
+	    WARN_ON(src_len < enc_len) ||
+	    WARN_ON(dst_len < enc_len + krb5->cksum_len))
+		return -EINVAL;
+
+	/* The message is laid out thusly:
+	 *
+	 *	Confounder||Data||Padding||Integrity
+	 *
+	 * The Padding may or may not be present, but the Integrity checksum
+	 * must be right at the end of the message so that we can find it.  The
+	 * Confounder, Data and Padding are encrypted; the Integrity checksum
+	 * is not.  The Integrity checksum is over the ciphertext.
+	 */
+	msg_len	= src_len;
+	enc_len	= msg_len - krb5->cksum_len;
+
+	bsize = krb5_shash_size(ctx->Ki) +
+		krb5_digest_size(ctx->Ki) +
+		krb5_sync_skcipher_size(ctx->Ke) +
+		krb5_sync_skcipher_ivsize(ctx->Ke);
+	bsize = umax(umax(bsize, krb5->conf_len), krb5->block_len);
+	buffer = kzalloc(bsize, GFP_NOFS);
+	if (!buffer)
+		return -ENOMEM;
+
+	/* Encrypt the secure region with key Ke. */
+	ci = buffer +
+		krb5_shash_size(ctx->Ki) +
+		krb5_digest_size(ctx->Ki);
+	iv = buffer +
+		krb5_shash_size(ctx->Ki) +
+		krb5_digest_size(ctx->Ki) +
+		krb5_sync_skcipher_size(ctx->Ke);
+
+	skcipher_request_set_sync_tfm(ci, ctx->Ke);
+	skcipher_request_set_callback(ci, 0, NULL, NULL);
+	skcipher_request_set_crypt(ci, src, dst, enc_len, iv);
+	ret = crypto_skcipher_encrypt(ci);
+	if (ret < 0)
+		goto error;
+
+	/* Calculate the checksum using key Ki */
+	cksum = buffer + krb5_shash_size(ctx->Ki);
+
+	desc = buffer;
+	desc->tfm = ctx->Ki;
+	ret = crypto_shash_init(desc);
+	if (ret < 0)
+		goto error;
+
+	memset(iv, 0, crypto_sync_skcipher_ivsize(ctx->Ke));
+	ret = crypto_shash_update(desc, iv, crypto_sync_skcipher_ivsize(ctx->Ke));
+	if (ret < 0)
+		goto error;
+
+	ret = crypto_shash_update_sg(desc, src, 0, enc_len);
+	if (ret < 0)
+		goto error;
+
+	ret = crypto_shash_final(desc, cksum);
+	if (ret < 0)
+		goto error;
+
+	/* Append the checksum into the buffer. */
+	ret = -EFAULT;
+	sg_zero_buffer(dst, nr_dst, 3, enc_len);
+	done = sg_pcopy_from_buffer(dst, nr_dst, cksum, krb5->cksum_len, enc_len);
+	if (done != krb5->cksum_len)
+		goto error;
+
+	ret = 0;
+error:
+	kfree_sensitive(buffer);
+	return ret;
+}
+
+/*
+ * Apply decryption and checksumming functions to part of an skbuff.  The
+ * offset and length are updated to reflect the actual content of the encrypted
+ * region.
+ *
+ * The associated data must contain a krb5_assoc_data struct.  At the
+ * conclusion, the output associated data is updated with the size of the
+ * encrypted data.  The associated data must be in its own scatterlist element
+ * in both chains so that we can skip over it.
+ */
+static int rfc8009_decrypt(struct aead_request *req)
+{
+	struct skcipher_request	*ci;
+	struct crypto_aead *tfm = crypto_aead_reqtfm(req);
+	struct krb5_enctype *krb5 = crypto_krb5_enctype(tfm);
+	struct scatterlist *src = req->src, *dst = req->dst;
+	struct shash_desc *desc;
+	struct krb5_ctx *ctx = crypto_krb5_ctx(tfm);
+	size_t bsize, src_len, dst_len, enc_len, msg_len = req->cryptlen, done;
+	void *buffer;
+	int ret, nr_src, nr_dst;
+	u8 *cksum, *cksum2, *iv;
+
+	src_len = sg_count(src, &nr_src);
+	dst_len = sg_count(dst, &nr_dst);
+
+	if (WARN_ON(msg_len < krb5->conf_len + krb5->cksum_len) ||
+	    WARN_ON(src_len < msg_len) ||
+	    WARN_ON(dst_len < msg_len - krb5->cksum_len))
+		return -EINVAL;
+
+	/* The integrity checksum is right up against the end. */
+	enc_len = msg_len - krb5->cksum_len;
+
+	bsize = krb5_shash_size(ctx->Ki) +
+		krb5_digest_size(ctx->Ki) * 2 +
+		krb5_sync_skcipher_size(ctx->Ke) +
+		krb5_sync_skcipher_ivsize(ctx->Ke);
+	buffer = kzalloc(bsize, GFP_NOFS);
+	if (!buffer)
+		return -ENOMEM;
+
+	cksum = buffer +
+		krb5_shash_size(ctx->Ki);
+	cksum2 = buffer +
+		krb5_shash_size(ctx->Ki) +
+		krb5_digest_size(ctx->Ki);
+	ci = buffer +
+		krb5_shash_size(ctx->Ki) +
+		krb5_digest_size(ctx->Ki) * 2;
+	iv = buffer +
+		krb5_shash_size(ctx->Ki) +
+		krb5_digest_size(ctx->Ki) * 2 +
+		krb5_sync_skcipher_size(ctx->Ke);
+
+	/* Calculate the checksum using key Ki */
+	desc = buffer;
+	desc->tfm = ctx->Ki;
+	ret = crypto_shash_init(desc);
+	if (ret < 0)
+		goto error;
+
+	ret = crypto_shash_update(desc, iv, crypto_sync_skcipher_ivsize(ctx->Ke));
+	if (ret < 0)
+		goto error;
+
+	ret = crypto_shash_update_sg(desc, src, 0, enc_len);
+	if (ret < 0)
+		goto error;
+
+	ret = crypto_shash_final(desc, cksum);
+	if (ret < 0)
+		goto error;
+
+	/* Get the checksum from the buffer. */
+	ret = -EFAULT;
+	done = sg_pcopy_to_buffer(src, nr_src, cksum2, krb5->cksum_len, enc_len);
+	if (done != krb5->cksum_len)
+		goto error;
+
+	ret = -EPROTO;
+	if (memcmp(cksum, cksum2, krb5->cksum_len) != 0)
+		goto error;
+
+	/* Decrypt the secure region with key Ke. */
+	skcipher_request_set_sync_tfm(ci, ctx->Ke);
+	skcipher_request_set_callback(ci, 0, NULL, NULL);
+	skcipher_request_set_crypt(ci, src, dst, enc_len, iv);
+	ret = crypto_skcipher_decrypt(ci);
+	if (ret < 0)
+		goto error;
+
+	ret = 0;
+error:
+	kfree_sensitive(buffer);
+	return ret;
+}
+
+int rfc8009_aead_encrypt(struct aead_request *req)
+{
+	struct crypto_aead *tfm = crypto_aead_reqtfm(req);
+	struct krb5_ctx *ctx = crypto_krb5_ctx(tfm);
+
+	if (ctx->Ke)
+		return rfc8009_encrypt(req);
+	return rfc3961_get_mic(req);
+}
+
+int rfc8009_aead_decrypt(struct aead_request *req)
+{
+	struct crypto_aead *tfm = crypto_aead_reqtfm(req);
+	struct krb5_ctx *ctx = crypto_krb5_ctx(tfm);
+
+	if (ctx->Ke)
+		return rfc8009_decrypt(req);
+	return rfc3961_verify_mic(req);
+}
+
+const struct krb5_crypto_profile rfc8009_crypto_profile = {
+	.calc_PRF	= rfc8009_calc_PRF,
+	.calc_Kc	= rfc8009_calc_Ki,
+	.calc_Ke	= rfc8009_calc_Ke,
+	.calc_Ki	= rfc8009_calc_Ki,
+};
diff --git a/crypto/krb5/selftest.c b/crypto/krb5/selftest.c
new file mode 100644
index 000000000000..b22bd8d00d1c
--- /dev/null
+++ b/crypto/krb5/selftest.c
@@ -0,0 +1,533 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Kerberos library self-testing
+ *
+ * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells at redhat.com)
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/slab.h>
+#include <crypto/skcipher.h>
+#include <crypto/hash.h>
+#include "internal.h"
+
+#define VALID(X) \
+	({								\
+		bool __x = (X);						\
+		if (__x) {						\
+			pr_warn("!!! TESTINVAL %s:%u\n", __FILE__, __LINE__); \
+			ret = -EBADMSG;					\
+		}							\
+		__x;							\
+	})
+
+#define CHECK(X) \
+	({								\
+		bool __x = (X);						\
+		if (__x) {						\
+			pr_warn("!!! TESTFAIL %s:%u\n", __FILE__, __LINE__); \
+			ret = -EBADMSG;					\
+		}							\
+		__x;							\
+	})
+
+enum which_key {
+	TEST_KC, TEST_KE, TEST_KI,
+};
+
+static int prep_buf(struct krb5_buffer *buf)
+{
+	buf->data = kmalloc(buf->len, GFP_KERNEL);
+	if (!buf->data)
+		return -ENOMEM;
+	return 0;
+}
+
+#define PREP_BUF(BUF, LEN)					\
+	do {							\
+		(BUF)->len = (LEN);				\
+		ret = prep_buf((BUF));				\
+		if (ret < 0)					\
+			goto out;				\
+	} while (0)
+
+static int load_buf(struct krb5_buffer *buf, const char *from)
+{
+	size_t len = strlen(from);
+	int ret;
+
+	if (len > 1 && from[0] == '\'') {
+		PREP_BUF(buf, len - 1);
+		memcpy(buf->data, from + 1, len - 1);
+		ret = 0;
+		goto out;
+	}
+
+	if (VALID(len & 1))
+		return -EINVAL;
+
+	PREP_BUF(buf, len / 2);
+	ret = hex2bin(buf->data, from, buf->len);
+	if (ret < 0) {
+		VALID(1);
+		goto out;
+	}
+out:
+	return ret;
+}
+
+#define LOAD_BUF(BUF, FROM) do { ret = load_buf(BUF, FROM); if (ret < 0) goto out; } while (0)
+
+static void clear_buf(struct krb5_buffer *buf)
+{
+	kfree(buf->data);
+	buf->len = 0;
+	buf->data = NULL;
+}
+
+/*
+ * Perform a pseudo-random function check.
+ */
+static int krb5_test_one_prf(const struct krb5_prf_test *test)
+{
+	const struct krb5_enctype *krb5 = crypto_krb5_find_enctype(test->etype);
+	struct krb5_buffer key = {}, octet = {}, result = {}, prf = {};
+	int ret;
+
+	if (!krb5)
+		return -EOPNOTSUPP;
+
+	pr_notice("Running %s %s\n", krb5->name, test->name);
+
+	LOAD_BUF(&key,   test->key);
+	LOAD_BUF(&octet, test->octet);
+	LOAD_BUF(&prf,   test->prf);
+	PREP_BUF(&result, krb5->prf_len);
+
+	if (VALID(result.len != prf.len)) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	ret = krb5->profile->calc_PRF(krb5, &key, &octet, &result, GFP_KERNEL);
+	if (ret < 0) {
+		CHECK(1);
+		pr_warn("PRF calculation failed %d\n", ret);
+		goto out;
+	}
+
+	if (memcmp(result.data, prf.data, result.len) != 0) {
+		CHECK(1);
+		ret = -EKEYREJECTED;
+		goto out;
+	}
+
+	ret = 0;
+
+out:
+	clear_buf(&result);
+	clear_buf(&octet);
+	clear_buf(&key);
+	return ret;
+}
+
+/*
+ * Perform a key derivation check.
+ */
+static int krb5_test_key(const struct krb5_enctype *krb5,
+			 const struct krb5_buffer *base_key,
+			 const struct krb5_key_test_one *test,
+			 enum which_key which)
+{
+	struct krb5_buffer key = {}, result = {};
+	int ret;
+
+	LOAD_BUF(&key,   test->key);
+	PREP_BUF(&result, key.len);
+
+	switch (which) {
+	case TEST_KC:
+		ret = krb5_derive_Kc(krb5, base_key, test->use, &result, GFP_KERNEL);
+		break;
+	case TEST_KE:
+		ret = krb5_derive_Ke(krb5, base_key, test->use, &result, GFP_KERNEL);
+		break;
+	case TEST_KI:
+		ret = krb5_derive_Ki(krb5, base_key, test->use, &result, GFP_KERNEL);
+		break;
+	default:
+		VALID(1);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (ret < 0) {
+		CHECK(1);
+		pr_warn("Key derivation failed %d\n", ret);
+		goto out;
+	}
+
+	if (memcmp(result.data, key.data, result.len) != 0) {
+		CHECK(1);
+		ret = -EKEYREJECTED;
+		goto out;
+	}
+
+out:
+	clear_buf(&key);
+	clear_buf(&result);
+	return ret;
+}
+
+static int krb5_test_one_key(const struct krb5_key_test *test)
+{
+	const struct krb5_enctype *krb5 = crypto_krb5_find_enctype(test->etype);
+	struct krb5_buffer base_key = {};
+	int ret;
+
+	if (!krb5)
+		return -EOPNOTSUPP;
+
+	pr_notice("Running %s %s\n", krb5->name, test->name);
+
+	LOAD_BUF(&base_key, test->key);
+
+	ret = krb5_test_key(krb5, &base_key, &test->Kc, TEST_KC);
+	if (ret < 0)
+		goto out;
+	ret = krb5_test_key(krb5, &base_key, &test->Ke, TEST_KE);
+	if (ret < 0)
+		goto out;
+	ret = krb5_test_key(krb5, &base_key, &test->Ki, TEST_KI);
+	if (ret < 0)
+		goto out;
+
+out:
+	clear_buf(&base_key);
+	return ret;
+}
+
+/*
+ * Perform an encryption test.
+ */
+static int krb5_test_one_enc(const struct krb5_enc_test *test, void *buf)
+{
+	const struct krb5_enctype *krb5 = crypto_krb5_find_enctype(test->etype);
+	struct aead_request *req = NULL;
+	struct crypto_aead *ci = NULL;
+	struct krb5_buffer key = {}, conf = {}, plain = {}, ct = {};
+	struct scatterlist sg[1];
+	size_t reqsize, data_len, data_offset, message_len;
+	int ret;
+
+	if (!krb5)
+		return -EOPNOTSUPP;
+
+	pr_notice("Running %s %s\n", krb5->name, test->name);
+
+	/* Load the test data into binary buffers. */
+	LOAD_BUF(&key, test->key);
+	LOAD_BUF(&conf, test->conf);
+	LOAD_BUF(&plain, test->plain);
+	LOAD_BUF(&ct, test->ct);
+
+	if (VALID(conf.len != krb5->conf_len) ||
+	    VALID(ct.len != krb5->conf_len + plain.len + krb5->cksum_len))
+		return ret;
+
+	data_len = plain.len;
+	message_len = crypto_krb5_how_much_buffer(krb5, KRB5_ENCRYPT_MODE,
+						  data_len, &data_offset);
+
+	if (CHECK(message_len != ct.len)) {
+		pr_warn("Encrypted length mismatch %zu != %u\n", message_len, ct.len);
+		goto out;
+	}
+
+	memcpy(buf + data_offset, plain.data, plain.len);
+
+	sg_init_one(sg, buf, message_len);
+	ret = crypto_krb5_confound_buffer(krb5, sg, 1, conf.data, conf.len, 0);
+	if (ret < 0) {
+		pr_err("Couldn't confound buffer %s: %d\n", krb5->aead.base.cra_name, ret);
+		goto out;
+	}
+
+	/* Allocate a crypto object and set its key. */
+	ci = crypto_alloc_aead(krb5->aead.base.cra_name, 0, 0);
+	if (IS_ERR(ci)) {
+		ret = (PTR_ERR(ci) == -ENOENT) ? -ENOPKG : PTR_ERR(ci);
+		ci = NULL;
+		pr_err("Couldn't alloc AEAD %s: %d\n", krb5->aead.base.cra_name, ret);
+		goto out;
+	}
+
+	ret = crypto_aead_setkey(ci, key.data, key.len);
+	if (ret < 0) {
+		pr_err("Couldn't set AEAD key %s: %d\n", krb5->aead.base.cra_name, ret);
+		goto out;
+	}
+
+	/* Generate an encryption request. */
+	reqsize = crypto_roundup(sizeof(*req) + crypto_aead_reqsize(ci));
+	req = kzalloc(reqsize, GFP_KERNEL);
+	if (!req)
+		goto out;
+
+	aead_request_set_tfm(req, ci);
+
+	sg_init_one(sg, buf, message_len);
+	aead_request_set_crypt(req, sg, sg, data_offset + data_len, NULL);
+
+	ret = crypto_aead_encrypt(req);
+	if (ret < 0) {
+		CHECK(1);
+		pr_warn("Encryption failed %d\n", ret);
+		goto out;
+	}
+
+	if (memcmp(buf, ct.data, ct.len) != 0) {
+		CHECK(1);
+		pr_warn("Ciphertext mismatch\n");
+		pr_warn("BUF %*phN\n", ct.len, buf);
+		pr_warn("CT  %*phN\n", ct.len, ct.data);
+		ret = -EKEYREJECTED;
+		goto out;
+	}
+
+	/* Generate a decryption request. */
+	memset(req, 0, reqsize);
+	aead_request_set_tfm(req, ci);
+
+	sg_init_one(sg, buf, message_len);
+	aead_request_set_crypt(req, sg, sg, message_len, NULL);
+
+	ret = crypto_aead_decrypt(req);
+	if (ret < 0) {
+		CHECK(1);
+		pr_warn("Decryption failed %d\n", ret);
+		goto out;
+	}
+
+	data_offset = 0;
+	data_len = message_len;
+	crypto_krb5_where_is_the_data(krb5, KRB5_ENCRYPT_MODE,
+				      &data_offset, &data_len);
+
+	if (CHECK(data_offset != conf.len) ||
+	    CHECK(data_len != plain.len))
+		goto out;
+
+	if (memcmp(buf, conf.data, conf.len) != 0) {
+		CHECK(1);
+		pr_warn("Confounder mismatch\n");
+		pr_warn("ENC %*phN\n", conf.len, buf);
+		pr_warn("DEC %*phN\n", conf.len, conf.data);
+		ret = -EKEYREJECTED;
+		goto out;
+	}
+
+	if (memcmp(buf + conf.len, plain.data, plain.len) != 0) {
+		CHECK(1);
+		pr_warn("Plaintext mismatch\n");
+		pr_warn("BUF %*phN\n", plain.len, buf + conf.len);
+		pr_warn("PT  %*phN\n", plain.len, plain.data);
+		ret = -EKEYREJECTED;
+		goto out;
+	}
+
+	ret = 0;
+
+out:
+	clear_buf(&ct);
+	clear_buf(&plain);
+	clear_buf(&conf);
+	clear_buf(&key);
+	aead_request_free(req);
+	if (ci)
+		crypto_free_aead(ci);
+	return ret;
+}
+
+/*
+ * Perform a checksum test.
+ */
+static int krb5_test_one_mic(const struct krb5_mic_test *test, void *buf)
+{
+	const struct krb5_enctype *krb5 = crypto_krb5_find_enctype(test->etype);
+	struct aead_request *req = NULL;
+	struct crypto_aead *ci = NULL;
+	struct krb5_buffer key = {}, plain = {}, mic = {};
+	struct scatterlist sg[2];
+	size_t reqsize, data_len, data_offset, message_len;
+	int ret;
+
+	if (!krb5)
+		return -EOPNOTSUPP;
+
+	pr_notice("Running %s %s\n", krb5->name, test->name);
+
+	/* Load the test data into binary buffers. */
+	LOAD_BUF(&key, test->key);
+	LOAD_BUF(&plain, test->plain);
+	LOAD_BUF(&mic, test->mic);
+
+	if (VALID(mic.len != krb5->cksum_len))
+		return ret;
+
+	data_len = plain.len;
+	message_len = crypto_krb5_how_much_buffer(krb5, KRB5_CHECKSUM_MODE,
+						  data_len, &data_offset);
+
+	if (CHECK(message_len != mic.len + plain.len)) {
+		pr_warn("MIC length mismatch %zu != %u\n",
+			message_len, mic.len + plain.len);
+		goto out;
+	}
+
+	memcpy(buf + data_offset, plain.data, plain.len);
+
+	/* Allocate a crypto object and set its key. */
+	ci = crypto_alloc_aead(krb5->aead.base.cra_name, 0, 0);
+	if (IS_ERR(ci)) {
+		ret = (PTR_ERR(ci) == -ENOENT) ? -ENOPKG : PTR_ERR(ci);
+		ci = NULL;
+		pr_err("Couldn't alloc AEAD %s: %d\n", krb5->aead.base.cra_name, ret);
+		goto out;
+	}
+
+	ret = crypto_aead_setkey(ci, key.data, key.len);
+	if (ret < 0) {
+		pr_err("Couldn't set AEAD key %s: %d\n", krb5->aead.base.cra_name, ret);
+		goto out;
+	}
+
+	/* Generate an encryption request. */
+	reqsize = sizeof(*req) + crypto_aead_reqsize(ci);
+	req = kzalloc(reqsize, GFP_KERNEL);
+	if (!req)
+		goto out;
+
+	aead_request_set_tfm(req, ci);
+
+	sg_init_one(sg, buf, 1024);
+	aead_request_set_crypt(req, sg, sg, data_offset + data_len, NULL);
+
+	ret = crypto_aead_encrypt(req);
+	if (ret < 0) {
+		CHECK(1);
+		pr_warn("Get MIC failed %d\n", ret);
+		goto out;
+	}
+
+	if (memcmp(buf, mic.data, mic.len) != 0) {
+		CHECK(1);
+		pr_warn("MIC mismatch\n");
+		pr_warn("BUF %*phN\n", mic.len, buf);
+		pr_warn("MIC %*phN\n", mic.len, mic.data);
+		ret = -EKEYREJECTED;
+		goto out;
+	}
+
+	/* Generate a decryption request. */
+	memset(req, 0, reqsize);
+
+	aead_request_set_tfm(req, ci);
+
+	sg_init_one(sg, buf, message_len);
+	aead_request_set_crypt(req, sg, sg, message_len, NULL);
+
+	ret = crypto_aead_decrypt(req);
+	if (ret < 0) {
+		CHECK(1);
+		pr_warn("Verify MIC failed %d\n", ret);
+		goto out;
+	}
+
+	data_offset = 0;
+	data_len = message_len;
+	crypto_krb5_where_is_the_data(krb5, KRB5_CHECKSUM_MODE,
+				      &data_offset, &data_len);
+
+	if (CHECK(data_offset != mic.len) ||
+	    CHECK(data_len != plain.len))
+		goto out;
+
+	if (memcmp(buf + data_offset, plain.data, plain.len) != 0) {
+		CHECK(1);
+		pr_warn("Plaintext mismatch\n");
+		pr_warn("BUF %*phN\n", plain.len, buf + data_offset);
+		pr_warn("PT  %*phN\n", plain.len, plain.data);
+		ret = -EKEYREJECTED;
+		goto out;
+	}
+
+	ret = 0;
+
+out:
+	clear_buf(&mic);
+	clear_buf(&plain);
+	clear_buf(&key);
+	aead_request_free(req);
+	if (ci)
+		crypto_free_aead(ci);
+	return ret;
+}
+
+int krb5_selftest(void)
+{
+	void *buf;
+	int ret = 0, i;
+
+	buf = kmalloc(4096, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	pr_notice("\n");
+	pr_notice("Running selftests\n");
+
+	for (i = 0; krb5_prf_tests[i].name; i++) {
+		ret = krb5_test_one_prf(&krb5_prf_tests[i]);
+		if (ret < 0) {
+			if (ret != -EOPNOTSUPP)
+				goto out;
+			pr_notice("Skipping %s\n", krb5_prf_tests[i].name);
+		}
+	}
+
+	for (i = 0; krb5_key_tests[i].name; i++) {
+		ret = krb5_test_one_key(&krb5_key_tests[i]);
+		if (ret < 0) {
+			if (ret != -EOPNOTSUPP)
+				goto out;
+			pr_notice("Skipping %s\n", krb5_key_tests[i].name);
+		}
+	}
+
+	for (i = 0; krb5_enc_tests[i].name; i++) {
+		memset(buf, 0x5a, 4096);
+		ret = krb5_test_one_enc(&krb5_enc_tests[i], buf);
+		if (ret < 0) {
+			if (ret != -EOPNOTSUPP)
+				goto out;
+			pr_notice("Skipping %s\n", krb5_enc_tests[i].name);
+		}
+	}
+
+	for (i = 0; krb5_mic_tests[i].name; i++) {
+		memset(buf, 0x5a, 4096);
+		ret = krb5_test_one_mic(&krb5_mic_tests[i], buf);
+		if (ret < 0) {
+			if (ret != -EOPNOTSUPP)
+				goto out;
+			pr_notice("Skipping %s\n", krb5_mic_tests[i].name);
+		}
+	}
+
+	ret = 0;
+out:
+	pr_notice("Selftests %s\n", ret == 0 ? "succeeded" : "failed");
+	kfree(buf);
+	return ret;
+}
diff --git a/crypto/krb5/selftest_data.c b/crypto/krb5/selftest_data.c
new file mode 100644
index 000000000000..7c35b1d7f4a2
--- /dev/null
+++ b/crypto/krb5/selftest_data.c
@@ -0,0 +1,370 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Data for Kerberos library self-testing
+ *
+ * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells at redhat.com)
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "internal.h"
+
+/*
+ * Pseudo-random function tests.
+ */
+const struct krb5_prf_test krb5_prf_tests[] = {
+	/* rfc8009 Appendix A */
+	{
+		.etype	= KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128,
+		.name	= "prf",
+		.key	= "3705D96080C17728A0E800EAB6E0D23C",
+		.octet	= "74657374",
+		.prf	= "9D188616F63852FE86915BB840B4A886FF3E6BB0F819B49B893393D393854295",
+	}, {
+		.etype	= KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192,
+		.name	= "prf",
+		.key	= "6D404D37FAF79F9DF0D33568D320669800EB4836472EA8A026D16B7182460C52",
+		.octet	= "74657374",
+		.prf	=
+		"9801F69A368C2BF675E59521E177D9A07F67EFE1CFDE8D3C8D6F6A0256E3B17D"
+		"B3C1B62AD1B8553360D17367EB1514D2",
+	},
+	{/* END */}
+};
+
+/*
+ * Key derivation tests.
+ */
+const struct krb5_key_test krb5_key_tests[] = {
+	/* rfc8009 Appendix A */
+	{
+		.etype	= KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128,
+		.name	= "key",
+		.key	= "3705D96080C17728A0E800EAB6E0D23C",
+		.Kc.use	= 0x00000002,
+		.Kc.key	= "B31A018A48F54776F403E9A396325DC3",
+		.Ke.use	= 0x00000002,
+		.Ke.key	= "9B197DD1E8C5609D6E67C3E37C62C72E",
+		.Ki.use	= 0x00000002,
+		.Ki.key	= "9FDA0E56AB2D85E1569A688696C26A6C",
+	}, {
+		.etype	= KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192,
+		.name	= "key",
+		.key	= "6D404D37FAF79F9DF0D33568D320669800EB4836472EA8A026D16B7182460C52",
+		.Kc.use	= 0x00000002,
+		.Kc.key	= "EF5718BE86CC84963D8BBB5031E9F5C4BA41F28FAF69E73D",
+		.Ke.use	= 0x00000002,
+		.Ke.key	= "56AB22BEE63D82D7BC5227F6773F8EA7A5EB1C825160C38312980C442E5C7E49",
+		.Ki.use	= 0x00000002,
+		.Ki.key	= "69B16514E3CD8E56B82010D5C73012B622C4D00FFC23ED1F",
+	},
+	/* rfc6803 sec 10 */
+	{
+		.etype	= KRB5_ENCTYPE_CAMELLIA128_CTS_CMAC,
+		.name	= "key",
+		.key	= "57D0297298FFD9D35DE5A47FB4BDE24B",
+		.Kc.use	= 0x00000002,
+		.Kc.key	= "D155775A209D05F02B38D42A389E5A56",
+		.Ke.use	= 0x00000002,
+		.Ke.key	= "64DF83F85A532F17577D8C37035796AB",
+		.Ki.use	= 0x00000002,
+		.Ki.key	= "3E4FBDF30FB8259C425CB6C96F1F4635",
+	},
+	{
+		.etype	= KRB5_ENCTYPE_CAMELLIA256_CTS_CMAC,
+		.name	= "key",
+		.key	= "B9D6828B2056B7BE656D88A123B1FAC68214AC2B727ECF5F69AFE0C4DF2A6D2C",
+		.Kc.use	= 0x00000002,
+		.Kc.key	= "E467F9A9552BC7D3155A6220AF9C19220EEED4FF78B0D1E6A1544991461A9E50",
+		.Ke.use	= 0x00000002,
+		.Ke.key	= "412AEFC362A7285FC3966C6A5181E7605AE675235B6D549FBFC9AB6630A4C604",
+		.Ki.use	= 0x00000002,
+		.Ki.key	= "FA624FA0E523993FA388AEFDC67E67EBCD8C08E8A0246B1D73B0D1DD9FC582B0",
+	},
+	{/* END */}
+};
+
+/*
+ * Encryption tests.
+ */
+const struct krb5_enc_test krb5_enc_tests[] = {
+	/* rfc8009 Appendix A */
+	{
+		.etype	= KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128,
+		.name	= "enc no plain",
+		.plain	= "",
+		.conf	= "7E5895EAF2672435BAD817F545A37148",
+		.key	=
+		"00000003" // KRB5_ENCRYPT_MODE_KEKI
+		"00000000" // Usage
+		"9B197DD1E8C5609D6E67C3E37C62C72E" // Ke
+		"9FDA0E56AB2D85E1569A688696C26A6C", // Ki
+		.ct	=
+		"EF85FB890BB8472F4DAB20394DCA781DAD877EDA39D50C870C0D5A0A8E48C718",
+	}, {
+		.etype	= KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128,
+		.name	= "enc plain<block",
+		.plain	= "000102030405",
+		.conf	= "7BCA285E2FD4130FB55B1A5C83BC5B24",
+		.key	=
+		"00000003" // KRB5_ENCRYPT_MODE_KEKI
+		"00000000" // Usage
+		"9B197DD1E8C5609D6E67C3E37C62C72E" // Ke
+		"9FDA0E56AB2D85E1569A688696C26A6C", // Ki
+		.ct	=
+		"84D7F30754ED987BAB0BF3506BEB09CFB55402CEF7E6877CE99E247E52D16ED4"
+		"421DFDF8976C",
+	}, {
+		.etype	= KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128,
+		.name	= "enc plain==block",
+		.plain	= "000102030405060708090A0B0C0D0E0F",
+		.conf	= "56AB21713FF62C0A1457200F6FA9948F",
+		.key	=
+		"00000003" // KRB5_ENCRYPT_MODE_KEKI
+		"00000000" // Usage
+		"9B197DD1E8C5609D6E67C3E37C62C72E" // Ke
+		"9FDA0E56AB2D85E1569A688696C26A6C", // Ki
+		.ct	=
+		"3517D640F50DDC8AD3628722B3569D2AE07493FA8263254080EA65C1008E8FC2"
+		"95FB4852E7D83E1E7C48C37EEBE6B0D3",
+	}, {
+		.etype	= KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128,
+		.name	= "enc plain>block",
+		.plain	= "000102030405060708090A0B0C0D0E0F1011121314",
+		.conf	= "A7A4E29A4728CE10664FB64E49AD3FAC",
+		.key	=
+		"00000003" // KRB5_ENCRYPT_MODE_KEKI
+		"00000000" // Usage
+		"9B197DD1E8C5609D6E67C3E37C62C72E" // Ke
+		"9FDA0E56AB2D85E1569A688696C26A6C", // Ki
+		.ct	=
+		"720F73B18D9859CD6CCB4346115CD336C70F58EDC0C4437C5573544C31C813BC"
+		"E1E6D072C186B39A413C2F92CA9B8334A287FFCBFC",
+	}, {
+		.etype	= KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192,
+		.name	= "enc no plain",
+		.plain	= "",
+		.conf	= "F764E9FA15C276478B2C7D0C4E5F58E4",
+		.key	=
+		"00000003" // KRB5_ENCRYPT_MODE_KEKI
+		"00000000" // Usage
+		"56AB22BEE63D82D7BC5227F6773F8EA7A5EB1C825160C38312980C442E5C7E49" // Ke
+		"69B16514E3CD8E56B82010D5C73012B622C4D00FFC23ED1F", // Ki
+		.ct	=
+		"41F53FA5BFE7026D91FAF9BE959195A058707273A96A40F0A01960621AC61274"
+		"8B9BBFBE7EB4CE3C",
+	}, {
+		.etype	= KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192,
+		.name	= "enc plain<block",
+		.plain	= "000102030405",
+		.conf	= "B80D3251C1F6471494256FFE712D0B9A",
+		.key	=
+		"00000003" // KRB5_ENCRYPT_MODE_KEKI
+		"00000000" // Usage
+		"56AB22BEE63D82D7BC5227F6773F8EA7A5EB1C825160C38312980C442E5C7E49" // Ke
+		"69B16514E3CD8E56B82010D5C73012B622C4D00FFC23ED1F", // Ki
+		.ct	=
+		"4ED7B37C2BCAC8F74F23C1CF07E62BC7B75FB3F637B9F559C7F664F69EAB7B60"
+		"92237526EA0D1F61CB20D69D10F2",
+	}, {
+		.etype	= KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192,
+		.name	= "enc plain==block",
+		.plain	= "000102030405060708090A0B0C0D0E0F",
+		.conf	= "53BF8A0D105265D4E276428624CE5E63",
+		.key	=
+		"00000003" // KRB5_ENCRYPT_MODE_KEKI
+		"00000000" // Usage
+		"56AB22BEE63D82D7BC5227F6773F8EA7A5EB1C825160C38312980C442E5C7E49" // Ke
+		"69B16514E3CD8E56B82010D5C73012B622C4D00FFC23ED1F", // Ki
+		.ct	=
+		"BC47FFEC7998EB91E8115CF8D19DAC4BBBE2E163E87DD37F49BECA92027764F6"
+		"8CF51F14D798C2273F35DF574D1F932E40C4FF255B36A266",
+	}, {
+		.etype	= KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192,
+		.name	= "enc plain>block",
+		.plain	= "000102030405060708090A0B0C0D0E0F1011121314",
+		.conf	= "763E65367E864F02F55153C7E3B58AF1",
+		.key	=
+		"00000003" // KRB5_ENCRYPT_MODE_KEKI
+		"00000000" // Usage
+		"56AB22BEE63D82D7BC5227F6773F8EA7A5EB1C825160C38312980C442E5C7E49" // Ke
+		"69B16514E3CD8E56B82010D5C73012B622C4D00FFC23ED1F", // Ki
+		.ct	=
+		"40013E2DF58E8751957D2878BCD2D6FE101CCFD556CB1EAE79DB3C3EE86429F2"
+		"B2A602AC86FEF6ECB647D6295FAE077A1FEB517508D2C16B4192E01F62",
+	},
+	/* rfc6803 sec 10 */
+	{
+		.etype	= KRB5_ENCTYPE_CAMELLIA128_CTS_CMAC,
+		.name	= "enc no plain",
+		.plain	= "",
+		.conf	= "B69822A19A6B09C0EBC8557D1F1B6C0A",
+		.key	=
+		"00000001" // KRB5_ENCRYPT_MODE
+		"00000000" // Usage
+		"1DC46A8D763F4F93742BCBA3387576C3", // K0
+		.ct	= "C466F1871069921EDB7C6FDE244A52DB0BA10EDC197BDB8006658CA3CCCE6EB8",
+	}, {
+		.etype	= KRB5_ENCTYPE_CAMELLIA128_CTS_CMAC,
+		.name	= "enc 1 plain",
+		.plain	= "'1",
+		.conf	= "6F2FC3C2A166FD8898967A83DE9596D9",
+		.key	=
+		"00000001" // KRB5_ENCRYPT_MODE
+		"00000001" // Usage
+		"5027BC231D0F3A9D23333F1CA6FDBE7C", // K0
+		.ct	= "842D21FD950311C0DD464A3F4BE8D6DA88A56D559C9B47D3F9A85067AF661559B8",
+	}, {
+		.etype	= KRB5_ENCTYPE_CAMELLIA128_CTS_CMAC,
+		.name	= "enc 9 plain",
+		.plain	= "'9 bytesss",
+		.conf	= "A5B4A71E077AEEF93C8763C18FDB1F10",
+		.key	=
+		"00000001" // KRB5_ENCRYPT_MODE
+		"00000002" // Usage
+		"A1BB61E805F9BA6DDE8FDBDDC05CDEA0", // K0
+		.ct	= "619FF072E36286FF0A28DEB3A352EC0D0EDF5C5160D663C901758CCF9D1ED33D71DB8F23AABF8348A0",
+	}, {
+		.etype	= KRB5_ENCTYPE_CAMELLIA128_CTS_CMAC,
+		.name	= "enc 13 plain",
+		.plain	= "'13 bytes byte",
+		.conf	= "19FEE40D810C524B5B22F01874C693DA",
+		.key	=
+		"00000001" // KRB5_ENCRYPT_MODE
+		"00000003" // Usage
+		"2CA27A5FAF5532244506434E1CEF6676", // K0
+		.ct	= "B8ECA3167AE6315512E59F98A7C500205E5F63FF3BB389AF1C41A21D640D8615C9ED3FBEB05AB6ACB67689B5EA",
+	}, {
+		.etype	= KRB5_ENCTYPE_CAMELLIA128_CTS_CMAC,
+		.name	= "enc 30 plain",
+		.plain	= "'30 bytes bytes bytes bytes byt",
+		.conf	= "CA7A7AB4BE192DABD603506DB19C39E2",
+		.key	=
+		"00000001" // KRB5_ENCRYPT_MODE
+		"00000004" // Usage
+		"7824F8C16F83FF354C6BF7515B973F43", // K0
+		.ct	= "A26A3905A4FFD5816B7B1E27380D08090C8EC1F304496E1ABDCD2BDCD1DFFC660989E117A713DDBB57A4146C1587CBA4356665591D2240282F5842B105A5",
+	}, {
+		.etype	= KRB5_ENCTYPE_CAMELLIA256_CTS_CMAC,
+		.name	= "enc no plain",
+		.plain	= "",
+		.conf	= "3CBBD2B45917941067F96599BB98926C",
+		.key	=
+		"00000001" // KRB5_ENCRYPT_MODE
+		"00000000" // Usage
+		"B61C86CC4E5D2757545AD423399FB7031ECAB913CBB900BD7A3C6DD8BF92015B", // K0
+		.ct	= "03886D03310B47A6D8F06D7B94D1DD837ECCE315EF652AFF620859D94A259266",
+	}, {
+		.etype	= KRB5_ENCTYPE_CAMELLIA256_CTS_CMAC,
+		.name	= "enc 1 plain",
+		.plain	= "'1",
+		.conf	= "DEF487FCEBE6DE6346D4DA4521BBA2D2",
+		.key	=
+		"00000001" // KRB5_ENCRYPT_MODE
+		"00000001" // Usage
+		"1B97FE0A190E2021EB30753E1B6E1E77B0754B1D684610355864104963463833", // K0
+		.ct	= "2C9C1570133C99BF6A34BC1B0212002FD194338749DB4135497A347CFCD9D18A12",
+	}, {
+		.etype	= KRB5_ENCTYPE_CAMELLIA256_CTS_CMAC,
+		.name	= "enc 9 plain",
+		.plain	= "'9 bytesss",
+		.conf	= "AD4FF904D34E555384B14100FC465F88",
+		.key	=
+		"00000001" // KRB5_ENCRYPT_MODE
+		"00000002" // Usage
+		"32164C5B434D1D1538E4CFD9BE8040FE8C4AC7ACC4B93D3314D2133668147A05", // K0
+		.ct	=
+		"9C6DE75F812DE7ED0D28B2963557A115640998275B0AF5152709913FF52A2A9C"
+		"8E63B872F92E64C839",
+	}, {
+		.etype	= KRB5_ENCTYPE_CAMELLIA256_CTS_CMAC,
+		.name	= "enc 13 plain",
+		.plain	= "'13 bytes byte",
+		.conf	= "CF9BCA6DF1144E0C0AF9B8F34C90D514",
+		.key	=
+		"00000001" // KRB5_ENCRYPT_MODE
+		"00000003" // Usage
+		"B038B132CD8E06612267FAB7170066D88AECCBA0B744BFC60DC89BCA182D0715", // K0
+		.ct	=
+		"EEEC85A9813CDC536772AB9B42DEFC5706F726E975DDE05A87EB5406EA324CA18"
+		"5C9986B42AABE794B84821BEE",
+	}, {
+		.etype	= KRB5_ENCTYPE_CAMELLIA256_CTS_CMAC,
+		.name	= "enc 30 plain",
+		.plain	= "'30 bytes bytes bytes bytes byt",
+		.conf	= "644DEF38DA35007275878D216855E228",
+		.key	=
+		"00000001" // KRB5_ENCRYPT_MODE
+		"00000004" // Usage
+		"CCFCD349BF4C6677E86E4B02B8EAB924A546AC731CF9BF6989B996E7D6BFBBA7", // K0
+		.ct	=
+		"0E44680985855F2D1F1812529CA83BFD8E349DE6FD9ADA0BAAA048D68E265FEB"
+		"F34AD1255A344999AD37146887A6C6845731AC7F46376A0504CD06571474",
+	},
+	{/* END */}
+};
+
+/*
+ * Checksum generation tests.
+ */
+const struct krb5_mic_test krb5_mic_tests[] = {
+	/* rfc8009 Appendix A */
+	{
+		.etype	= KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128,
+		.name	= "mic",
+		.plain	= "000102030405060708090A0B0C0D0E0F1011121314",
+		.key	=
+		"00000002" // KRB5_ENCRYPT_MODE_KC
+		"00000000" // Usage
+		"B31A018A48F54776F403E9A396325DC3", // Kc
+		.mic	= "D78367186643D67B411CBA9139FC1DEE",
+	}, {
+		.etype	= KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192,
+		.name	= "mic",
+		.plain	= "000102030405060708090A0B0C0D0E0F1011121314",
+		.key	=
+		"00000002" // KRB5_ENCRYPT_MODE_KC
+		"00000000" // Usage
+		"EF5718BE86CC84963D8BBB5031E9F5C4BA41F28FAF69E73D", // Kc
+		.mic	= "45EE791567EEFCA37F4AC1E0222DE80D43C3BFA06699672A",
+	},
+	/* rfc6803 sec 10 */
+	{
+		.etype	= KRB5_ENCTYPE_CAMELLIA128_CTS_CMAC,
+		.name	= "mic abc",
+		.plain	= "'abcdefghijk",
+		.key	=
+		"00000000" // KRB5_ENCRYPT_MODE
+		"00000007" // Usage
+		"1DC46A8D763F4F93742BCBA3387576C3", // K0
+		.mic	= "1178E6C5C47A8C1AE0C4B9C7D4EB7B6B",
+	}, {
+		.etype	= KRB5_ENCTYPE_CAMELLIA128_CTS_CMAC,
+		.name	= "mic ABC",
+		.plain	= "'ABCDEFGHIJKLMNOPQRSTUVWXYZ",
+		.key	=
+		"00000000" // KRB5_ENCRYPT_MODE
+		"00000008" // Usage
+		"5027BC231D0F3A9D23333F1CA6FDBE7C", // K0
+		.mic	= "D1B34F7004A731F23A0C00BF6C3F753A",
+	}, {
+		.etype	= KRB5_ENCTYPE_CAMELLIA256_CTS_CMAC,
+		.name	= "mic 123",
+		.plain	= "'123456789",
+		.key	=
+		"00000000" // KRB5_ENCRYPT_MODE
+		"00000009" // Usage
+		"B61C86CC4E5D2757545AD423399FB7031ECAB913CBB900BD7A3C6DD8BF92015B", // K0
+		.mic	= "87A12CFD2B96214810F01C826E7744B1",
+	}, {
+		.etype	= KRB5_ENCTYPE_CAMELLIA256_CTS_CMAC,
+		.name	= "mic !@#",
+		.plain	= "'!@#$%^&*()!@#$%^&*()!@#$%^&*()",
+		.key	=
+		"00000000" // KRB5_ENCRYPT_MODE
+		"0000000a" // Usage
+		"32164C5B434D1D1538E4CFD9BE8040FE8C4AC7ACC4B93D3314D2133668147A05", // K0
+		.mic	= "3FA0B42355E52B189187294AA252AB64",
+	},
+	{/* END */}
+};
diff --git a/include/crypto/krb5.h b/include/crypto/krb5.h
index 05e80fad2b38..359aba2076de 100644
--- a/include/crypto/krb5.h
+++ b/include/crypto/krb5.h
@@ -8,8 +8,12 @@
 #ifndef _CRYPTO_KRB5_H
 #define _CRYPTO_KRB5_H
 
-/* per Kerberos v5 protocol spec crypto types from the wire.
- * these get mapped to linux kernel crypto routines.
+#include <linux/crypto.h>
+#include <crypto/aead.h>
+
+/*
+ * Per Kerberos v5 protocol spec crypto types from the wire.  These get mapped
+ * to linux kernel crypto routines.
  */
 #define KRB5_ENCTYPE_NULL			0x0000
 #define KRB5_ENCTYPE_DES_CBC_CRC		0x0001	/* DES cbc mode with CRC-32 */
@@ -23,8 +27,12 @@
 #define KRB5_ENCTYPE_DES3_CBC_SHA1		0x0010
 #define KRB5_ENCTYPE_AES128_CTS_HMAC_SHA1_96	0x0011
 #define KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96	0x0012
+#define KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128	0x0013
+#define KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192	0x0014
 #define KRB5_ENCTYPE_ARCFOUR_HMAC		0x0017
 #define KRB5_ENCTYPE_ARCFOUR_HMAC_EXP		0x0018
+#define KRB5_ENCTYPE_CAMELLIA128_CTS_CMAC	0x0019
+#define KRB5_ENCTYPE_CAMELLIA256_CTS_CMAC	0x001a
 #define KRB5_ENCTYPE_UNKNOWN			0x01ff
 
 #define KRB5_CKSUMTYPE_CRC32			0x0001
@@ -37,6 +45,10 @@
 #define KRB5_CKSUMTYPE_HMAC_SHA1_DES3		0x000c
 #define KRB5_CKSUMTYPE_HMAC_SHA1_96_AES128	0x000f
 #define KRB5_CKSUMTYPE_HMAC_SHA1_96_AES256	0x0010
+#define KRB5_CKSUMTYPE_CMAC_CAMELLIA128		0x0011
+#define KRB5_CKSUMTYPE_CMAC_CAMELLIA256		0x0012
+#define KRB5_CKSUMTYPE_HMAC_SHA256_128_AES128	0x0013
+#define KRB5_CKSUMTYPE_HMAC_SHA384_192_AES256	0x0014
 #define KRB5_CKSUMTYPE_HMAC_MD5_ARCFOUR		-138 /* Microsoft md5 hmac cksumtype */
 
 /*
@@ -47,4 +59,89 @@
 #define KEY_USAGE_SEED_ENCRYPTION       (0xAA)
 #define KEY_USAGE_SEED_INTEGRITY        (0x55)
 
+/*
+ * Mode of operation.
+ */
+enum krb5_crypto_mode {
+	KRB5_CHECKSUM_MODE,	/* Checksum only */
+	KRB5_ENCRYPT_MODE,	/* Fully encrypted, possibly with integrity checksum */
+	KRB5_CHECKSUM_MODE_KC,	/* Checksum only, keys Kc supplied directly */
+	KRB5_ENCRYPT_MODE_KEKI,	/* Fully encrypted, keys Ke and Ki supplied directly */
+};
+
+struct krb5_buffer {
+	unsigned int	len;
+	void		*data;
+};
+
+/*
+ * Kerberos encoding type definition.
+ */
+struct krb5_enctype {
+	struct aead_alg	aead;		/* AEAD API */
+	int		etype;		/* Encryption (key) type */
+	int		ctype;		/* Checksum type */
+	const char	*name;		/* "Friendly" name */
+	const char	*encrypt_name;	/* Crypto encrypt name */
+	const char	*cksum_name;	/* Crypto checksum name */
+	const char	*hash_name;	/* Crypto hash name */
+	u16		block_len;	/* Length of encryption block */
+	u16		conf_len;	/* Length of confounder (normally == block_len) */
+	u16		cksum_len;	/* Length of checksum */
+	u16		key_bytes;	/* Length of raw key, in bytes */
+	u16		key_len;	/* Length of final key, in bytes */
+	u16		hash_len;	/* Length of hash in bytes */
+	u16		prf_len;	/* Length of PRF() result in bytes */
+	u16		Kc_len;		/* Length of Kc in bytes */
+	u16		Ke_len;		/* Length of Ke in bytes */
+	u16		Ki_len;		/* Length of Ki in bytes */
+	bool		keyed_cksum;	/* T if a keyed cksum */
+
+	const struct krb5_crypto_profile *profile;
+
+	int (*random_to_key)(const struct krb5_enctype *krb5,
+			     const struct krb5_buffer *in,
+			     struct krb5_buffer *out);	/* complete key generation */
+};
+
+/**
+ * crypto_krb5_enctype - Find the encoding type definition from the algorithm
+ * @tfm: The algorithm to query
+ */
+static inline struct krb5_enctype *crypto_krb5_enctype(const struct crypto_aead *tfm)
+{
+	struct aead_alg *alg = crypto_aead_alg((struct crypto_aead *)tfm);
+
+	return container_of(alg, struct krb5_enctype, aead);
+}
+
+/*
+ * krb5_aead.c
+ */
+const struct krb5_enctype *crypto_krb5_find_enctype(u32 enctype);
+
+size_t crypto_krb5_how_much_buffer(const struct krb5_enctype *krb5,
+				   enum krb5_crypto_mode mode,
+				   size_t data_size, size_t *_offset);
+size_t crypto_krb5_how_much_data(const struct krb5_enctype *krb5,
+				 enum krb5_crypto_mode mode,
+				 size_t *_buffer_size, size_t *_offset);
+void crypto_krb5_where_is_the_data(const struct krb5_enctype *krb5,
+				   enum krb5_crypto_mode mode,
+				   size_t *_offset, size_t *_len);
+int crypto_krb5_confound_buffer(const struct krb5_enctype *krb5,
+				struct scatterlist *sg, unsigned int nr_sg,
+				const u8 *confounder, size_t conf_len,
+				size_t msg_offset);
+
+/*
+ * kdf.c
+ */
+int crypto_krb5_calc_PRFplus(const struct krb5_enctype *krb5,
+			     const struct krb5_buffer *K,
+			     unsigned int L,
+			     const struct krb5_buffer *S,
+			     struct krb5_buffer *result,
+			     gfp_t gfp);
+
 #endif /* _CRYPTO_KRB5_H */



From dhowells at redhat.com  Thu Jan  9 17:03:05 2025
From: dhowells at redhat.com (David Howells)
Date: Fri, 10 Jan 2025 01:03:05 +0000
Subject: [RFC PATCH 3/8] crypto/krb5: Test manager data
In-Reply-To: <20250110010313.1471063-1-dhowells@redhat.com>
References: <20250110010313.1471063-1-dhowells@redhat.com>
Message-ID: <20250110010313.1471063-4-dhowells@redhat.com>

Add Kerberos crypto tests to the test manager database.  This covers:

	camellia128-cts-cmac		samples from RFC6803
	camellia256-cts-cmac		samples from RFC6803
	aes128-cts-hmac-sha256-128	samples from RFC8009
	aes256-cts-hmac-sha384-192	samples from RFC8009

but not:

	aes128-cts-hmac-sha1-96
	aes256-cts-hmac-sha1-96

as the test samples in RFC3962 don't seem to be suitable.

Note that the test manager makes some assumptions about AEAD algorithm type
that would otherwise prevent testing the kerberos algorithms.  The problem
is that the test manager assumes that if there's a difference between the
length of the plain text and the cipher text supplied in the test vector,
then the extra data is the authentication tag - but these aren't applicable
here.

This is worked around by adding a flag in the AEAD algorithm definition
that causes EINVAL be returned unconditionally if anyone tries to set the
auth tag length.

Signed-off-by: David Howells <dhowells at redhat.com>
cc: Herbert Xu <herbert at gondor.apana.org.au>
cc: "David S. Miller" <davem at davemloft.net>
cc: Chuck Lever <chuck.lever at oracle.com>
cc: Marc Dionne <marc.dionne at auristor.com>
cc: Eric Dumazet <edumazet at google.com>
cc: Jakub Kicinski <kuba at kernel.org>
cc: Paolo Abeni <pabeni at redhat.com>
cc: Simon Horman <horms at kernel.org>
cc: linux-afs at lists.infradead.org
cc: linux-nfs at vger.kernel.org
cc: linux-crypto at vger.kernel.org
cc: netdev at vger.kernel.org
---
 crypto/aead.c           |   2 +
 crypto/krb5/krb5_aead.c |   8 +-
 crypto/testmgr.c        |  24 +++
 crypto/testmgr.h        | 456 ++++++++++++++++++++++++++++++++++++++++
 include/crypto/aead.h   |   2 +
 5 files changed, 491 insertions(+), 1 deletion(-)

diff --git a/crypto/aead.c b/crypto/aead.c
index cade532413bf..fb23557cd09a 100644
--- a/crypto/aead.c
+++ b/crypto/aead.c
@@ -65,6 +65,8 @@ int crypto_aead_setauthsize(struct crypto_aead *tfm, unsigned int authsize)
 {
 	int err;
 
+	if (crypto_aead_alg(tfm)->no_authtags)
+		return -EINVAL;
 	if ((!authsize && crypto_aead_maxauthsize(tfm)) ||
 	    authsize > crypto_aead_maxauthsize(tfm))
 		return -EINVAL;
diff --git a/crypto/krb5/krb5_aead.c b/crypto/krb5/krb5_aead.c
index 2c8b3921e976..453b16a17ca9 100644
--- a/crypto/krb5/krb5_aead.c
+++ b/crypto/krb5/krb5_aead.c
@@ -201,6 +201,7 @@ static struct krb5_enctype krb5_enctypes[] = {
 		.aead.ivsize		= 0,
 		.aead.maxauthsize	= 0,
 		.aead.chunksize		= 16,
+		.aead.no_authtags	= true,
 
 		.aead.base.cra_flags		= CRYPTO_ALG_ALLOCATES_MEMORY,
 		.aead.base.cra_blocksize	= 1,
@@ -238,6 +239,7 @@ static struct krb5_enctype krb5_enctypes[] = {
 		.aead.ivsize		= 0,
 		.aead.maxauthsize	= 0,
 		.aead.chunksize		= 16,
+		.aead.no_authtags	= true,
 
 		.aead.base.cra_flags		= CRYPTO_ALG_ALLOCATES_MEMORY,
 		.aead.base.cra_blocksize	= 1,
@@ -275,6 +277,7 @@ static struct krb5_enctype krb5_enctypes[] = {
 		.aead.ivsize		= 0,
 		.aead.maxauthsize	= 0,
 		.aead.chunksize		= 16,
+		.aead.no_authtags	= true,
 
 		.aead.base.cra_flags		= CRYPTO_ALG_ALLOCATES_MEMORY,
 		.aead.base.cra_blocksize	= 1,
@@ -312,6 +315,7 @@ static struct krb5_enctype krb5_enctypes[] = {
 		.aead.ivsize		= 0,
 		.aead.maxauthsize	= 0,
 		.aead.chunksize		= 16,
+		.aead.no_authtags	= true,
 
 		.aead.base.cra_flags		= CRYPTO_ALG_ALLOCATES_MEMORY,
 		.aead.base.cra_blocksize	= 1,
@@ -349,6 +353,7 @@ static struct krb5_enctype krb5_enctypes[] = {
 		.aead.ivsize		= 0,
 		.aead.maxauthsize	= 0,
 		.aead.chunksize		= 16,
+		.aead.no_authtags	= true,
 
 		.aead.base.cra_flags		= CRYPTO_ALG_ALLOCATES_MEMORY,
 		.aead.base.cra_blocksize	= 1,
@@ -356,7 +361,7 @@ static struct krb5_enctype krb5_enctypes[] = {
 		.aead.base.cra_alignmask	= 0,
 		.aead.base.cra_priority		= 100,
 		.aead.base.cra_name		= "krb5-aes128-cts-hmac-sha256-128",
-		.aead.base.cra_driver_name	= "krb5-aes128-cts-hmac-sha256-128generic",
+		.aead.base.cra_driver_name	= "krb5-aes128-cts-hmac-sha256-128-generic",
 		.aead.base.cra_module		= THIS_MODULE,
 	}, {
 		.etype			= KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192,
@@ -386,6 +391,7 @@ static struct krb5_enctype krb5_enctypes[] = {
 		.aead.ivsize		= 0,
 		.aead.maxauthsize	= 0,
 		.aead.chunksize		= 16,
+		.aead.no_authtags	= true,
 
 		.aead.base.cra_flags		= CRYPTO_ALG_ALLOCATES_MEMORY,
 		.aead.base.cra_blocksize	= 1,
diff --git a/crypto/testmgr.c b/crypto/testmgr.c
index 1f5f48ab18c7..6e4cf8427e4e 100644
--- a/crypto/testmgr.c
+++ b/crypto/testmgr.c
@@ -5408,6 +5408,30 @@ static const struct alg_test_desc alg_test_descs[] = {
 		.alg = "jitterentropy_rng",
 		.fips_allowed = 1,
 		.test = alg_test_null,
+	}, {
+		.alg = "krb5-aes128-cts-hmac-sha256-128",
+		.test = alg_test_aead,
+		.suite = {
+			.aead = __VECS(krb5_test_aes128_cts_hmac_sha256_128)
+		}
+	}, {
+		.alg = "krb5-aes256-cts-hmac-sha384-192",
+		.test = alg_test_aead,
+		.suite = {
+			.aead = __VECS(krb5_test_aes256_cts_hmac_sha384_192)
+		}
+	}, {
+		.alg = "krb5-camellia128-cts-cmac",
+		.test = alg_test_aead,
+		.suite = {
+			.aead = __VECS(krb5_test_camellia128_cts_cmac)
+		}
+	}, {
+		.alg = "krb5-camellia256-cts-cmac",
+		.test = alg_test_aead,
+		.suite = {
+			.aead = __VECS(krb5_test_camellia256_cts_cmac)
+		}
 	}, {
 		.alg = "kw(aes)",
 		.test = alg_test_skcipher,
diff --git a/crypto/testmgr.h b/crypto/testmgr.h
index 430d33d9ac13..12c550248a15 100644
--- a/crypto/testmgr.h
+++ b/crypto/testmgr.h
@@ -39086,4 +39086,460 @@ static const struct cipher_testvec aes_hctr2_tv_template[] = {
 
 };
 
+static const struct aead_testvec krb5_test_aes128_cts_hmac_sha256_128[] = {
+	/* rfc8009 Appendix A */
+	{
+		/* "enc no plain" */
+		.key	=
+		"\x00\x00\x00\x03" // KRB5_ENCRYPT_MODE_KEKI
+		"\x00\x00\x00\x00" // Usage
+		"\x9B\x19\x7D\xD1\xE8\xC5\x60\x9D\x6E\x67\xC3\xE3\x7C\x62\xC7\x2E" // Ke
+		"\x9F\xDA\x0E\x56\xAB\x2D\x85\xE1\x56\x9A\x68\x86\x96\xC2\x6A\x6C", // Ki
+		.klen	= 4 + 4 + 16 + 16,
+		.ptext	=
+		"\x7E\x58\x95\xEA\xF2\x67\x24\x35\xBA\xD8\x17\xF5\x45\xA3\x71\x48" // Confounder
+		"", // Plain
+		.plen	= 16 + 0,
+		.ctext	=
+		"\xEF\x85\xFB\x89\x0B\xB8\x47\x2F\x4D\xAB\x20\x39\x4D\xCA\x78\x1D"
+		"\xAD\x87\x7E\xDA\x39\xD5\x0C\x87\x0C\x0D\x5A\x0A\x8E\x48\xC7\x18",
+		.clen	= 16 + 0 + 16,
+		.setauthsize_error = -EINVAL,
+	}, {
+		/* "enc plain<block" */
+		.key	=
+		"\x00\x00\x00\x03" // KRB5_ENCRYPT_MODE_KEKI
+		"\x00\x00\x00\x00" // Usage
+		"\x9B\x19\x7D\xD1\xE8\xC5\x60\x9D\x6E\x67\xC3\xE3\x7C\x62\xC7\x2E" // Ke
+		"\x9F\xDA\x0E\x56\xAB\x2D\x85\xE1\x56\x9A\x68\x86\x96\xC2\x6A\x6C", // Ki
+		.klen	= 4 + 4 + 16 + 16,
+		.ptext	=
+		"\x7B\xCA\x28\x5E\x2F\xD4\x13\x0F\xB5\x5B\x1A\x5C\x83\xBC\x5B\x24" // Confounder
+		"\x00\x01\x02\x03\x04\x05", // Plain
+		.plen	= 16 + 6,
+		.ctext	=
+		"\x84\xD7\xF3\x07\x54\xED\x98\x7B\xAB\x0B\xF3\x50\x6B\xEB\x09\xCF"
+		"\xB5\x54\x02\xCE\xF7\xE6\x87\x7C\xE9\x9E\x24\x7E\x52\xD1\x6E\xD4"
+		"\x42\x1D\xFD\xF8\x97\x6C",
+		.clen	= 16 + 6 + 16,
+		.setauthsize_error = -EINVAL,
+	}, {
+		/* "enc plain==block" */
+		.key	=
+		"\x00\x00\x00\x03" // KRB5_ENCRYPT_MODE_KEKI
+		"\x00\x00\x00\x00" // Usage
+		"\x9B\x19\x7D\xD1\xE8\xC5\x60\x9D\x6E\x67\xC3\xE3\x7C\x62\xC7\x2E" // Ke
+		"\x9F\xDA\x0E\x56\xAB\x2D\x85\xE1\x56\x9A\x68\x86\x96\xC2\x6A\x6C", // Ki
+		.klen	= 4 + 4 + 16 + 16,
+		.ptext	=
+		"\x56\xAB\x21\x71\x3F\xF6\x2C\x0A\x14\x57\x20\x0F\x6F\xA9\x94\x8F" // Confounder
+		"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", // Plain
+		.plen	= 16 + 16,
+		.ctext	=
+		"\x35\x17\xD6\x40\xF5\x0D\xDC\x8A\xD3\x62\x87\x22\xB3\x56\x9D\x2A"
+		"\xE0\x74\x93\xFA\x82\x63\x25\x40\x80\xEA\x65\xC1\x00\x8E\x8F\xC2"
+		"\x95\xFB\x48\x52\xE7\xD8\x3E\x1E\x7C\x48\xC3\x7E\xEB\xE6\xB0\xD3",
+		.clen	= 16 + 16 + 16,
+		.setauthsize_error = -EINVAL,
+	}, {
+		/* "enc plain>block" */
+		.key	=
+		"\x00\x00\x00\x03" // KRB5_ENCRYPT_MODE_KEKI
+		"\x00\x00\x00\x00" // Usage
+		"\x9B\x19\x7D\xD1\xE8\xC5\x60\x9D\x6E\x67\xC3\xE3\x7C\x62\xC7\x2E" // Ke
+		"\x9F\xDA\x0E\x56\xAB\x2D\x85\xE1\x56\x9A\x68\x86\x96\xC2\x6A\x6C", // Ki
+		.klen	= 4 + 4 + 16 + 16,
+		.ptext	=
+		"\xA7\xA4\xE2\x9A\x47\x28\xCE\x10\x66\x4F\xB6\x4E\x49\xAD\x3F\xAC" // Confounder
+		"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
+		"\x10\x11\x12\x13\x14", // Plain
+		.plen	= 16 + 21,
+		.ctext	=
+		"\x72\x0F\x73\xB1\x8D\x98\x59\xCD\x6C\xCB\x43\x46\x11\x5C\xD3\x36"
+		"\xC7\x0F\x58\xED\xC0\xC4\x43\x7C\x55\x73\x54\x4C\x31\xC8\x13\xBC"
+		"\xE1\xE6\xD0\x72\xC1\x86\xB3\x9A\x41\x3C\x2F\x92\xCA\x9B\x83\x34"
+		"\xA2\x87\xFF\xCB\xFC",
+		.clen	= 16 + 21 + 16,
+		.setauthsize_error = -EINVAL,
+	}, {
+		// "mic"
+		.key	=
+		"\x00\x00\x00\x02" // KR5_ENCRYPT_MODE_KC
+		"\x00\x00\x00\x00" // Usage
+		"\xB3\x1A\x01\x8A\x48\xF5\x47\x76\xF4\x03\xE9\xA3\x96\x32\x5D\xC3", // Kc
+		.klen	= 4 + 4 + 16,
+		.ptext	=
+		"\xAA\x55\xAA\x55\xAA\x55\xAA\x55\xAA\x55\xAA\x55\xAA\x55\xAA\x55" // MIC
+		"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
+		"\x10\x11\x12\x13\x14",
+		.plen	= 16 + 21,
+		.ctext	=
+		"\xD7\x83\x67\x18\x66\x43\xD6\x7B\x41\x1C\xBA\x91\x39\xFC\x1D\xEE" // MIC
+		"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
+		"\x10\x11\x12\x13\x14",
+		.clen	= 16 + 21,
+		.setauthsize_error = -EINVAL,
+	}
+};
+
+static const struct aead_testvec krb5_test_aes256_cts_hmac_sha384_192[] = {
+	/* rfc8009 Appendix A */
+	{
+		/* "enc no plain" */
+		.key	=
+		"\x00\x00\x00\x03" // KRB5_ENCRYPT_MODE_KEKI
+		"\x00\x00\x00\x00" // Usage
+		"\x56\xAB\x22\xBE\xE6\x3D\x82\xD7\xBC\x52\x27\xF6\x77\x3F\x8E\xA7"
+		"\xA5\xEB\x1C\x82\x51\x60\xC3\x83\x12\x98\x0C\x44\x2E\x5C\x7E\x49" // Ke
+		"\x69\xB1\x65\x14\xE3\xCD\x8E\x56\xB8\x20\x10\xD5\xC7\x30\x12\xB6"
+		"\x22\xC4\xD0\x0F\xFC\x23\xED\x1F", // Ki
+		.klen	= 4 + 4 + 32 + 24,
+		.ptext	=
+		"\xF7\x64\xE9\xFA\x15\xC2\x76\x47\x8B\x2C\x7D\x0C\x4E\x5F\x58\xE4" // Confounder
+		"", // Plain
+		.plen	= 16 + 0,
+		.ctext	=
+		"\x41\xF5\x3F\xA5\xBF\xE7\x02\x6D\x91\xFA\xF9\xBE\x95\x91\x95\xA0"
+		"\x58\x70\x72\x73\xA9\x6A\x40\xF0\xA0\x19\x60\x62\x1A\xC6\x12\x74"
+		"\x8B\x9B\xBF\xBE\x7E\xB4\xCE\x3C",
+		.clen	= 16 + 0 + 24,
+		.setauthsize_error = -EINVAL,
+	}, {
+		/* "enc plain<block" */
+		.key	=
+		"\x00\x00\x00\x03" // KRB5_ENCRYPT_MODE_KEKI
+		"\x00\x00\x00\x00" // Usage
+		"\x56\xAB\x22\xBE\xE6\x3D\x82\xD7\xBC\x52\x27\xF6\x77\x3F\x8E\xA7"
+		"\xA5\xEB\x1C\x82\x51\x60\xC3\x83\x12\x98\x0C\x44\x2E\x5C\x7E\x49" // Ke
+		"\x69\xB1\x65\x14\xE3\xCD\x8E\x56\xB8\x20\x10\xD5\xC7\x30\x12\xB6"
+		"\x22\xC4\xD0\x0F\xFC\x23\xED\x1F", // Ki
+		.klen	= 4 + 4 + 32 + 24,
+		.ptext	=
+		"\xB8\x0D\x32\x51\xC1\xF6\x47\x14\x94\x25\x6F\xFE\x71\x2D\x0B\x9A" // Confounder
+		"\x00\x01\x02\x03\x04\x05", // Plain
+		.plen	= 16 + 0,
+		.ctext	=
+		"\x4E\xD7\xB3\x7C\x2B\xCA\xC8\xF7\x4F\x23\xC1\xCF\x07\xE6\x2B\xC7"
+		"\xB7\x5F\xB3\xF6\x37\xB9\xF5\x59\xC7\xF6\x64\xF6\x9E\xAB\x7B\x60"
+		"\x92\x23\x75\x26\xEA\x0D\x1F\x61\xCB\x20\xD6\x9D\x10\xF2",
+		.clen	= 16 + 0 + 24,
+		.setauthsize_error = -EINVAL,
+	}, {
+		/* "enc plain==block" */
+		.key	=
+		"\x00\x00\x00\x03" // KRB5_ENCRYPT_MODE_KEKI
+		"\x00\x00\x00\x00" // Usage
+		"\x56\xAB\x22\xBE\xE6\x3D\x82\xD7\xBC\x52\x27\xF6\x77\x3F\x8E\xA7"
+		"\xA5\xEB\x1C\x82\x51\x60\xC3\x83\x12\x98\x0C\x44\x2E\x5C\x7E\x49" // Ke
+		"\x69\xB1\x65\x14\xE3\xCD\x8E\x56\xB8\x20\x10\xD5\xC7\x30\x12\xB6"
+		"\x22\xC4\xD0\x0F\xFC\x23\xED\x1F", // Ki
+		.klen	= 4 + 4 + 32 + 24,
+		.ptext	=
+		"\x53\xBF\x8A\x0D\x10\x52\x65\xD4\xE2\x76\x42\x86\x24\xCE\x5E\x63" // Confounder
+		"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", // Plain
+		.plen	= 16 + 16,
+		.ctext	=
+		"\xBC\x47\xFF\xEC\x79\x98\xEB\x91\xE8\x11\x5C\xF8\xD1\x9D\xAC\x4B"
+		"\xBB\xE2\xE1\x63\xE8\x7D\xD3\x7F\x49\xBE\xCA\x92\x02\x77\x64\xF6"
+		"\x8C\xF5\x1F\x14\xD7\x98\xC2\x27\x3F\x35\xDF\x57\x4D\x1F\x93\x2E"
+		"\x40\xC4\xFF\x25\x5B\x36\xA2\x66",
+		.clen	= 16 + 16 + 24,
+		.setauthsize_error = -EINVAL,
+	}, {
+		/* "enc plain>block" */
+		.key	=
+		"\x00\x00\x00\x03" // KRB5_ENCRYPT_MODE_KEKI
+		"\x00\x00\x00\x00" // Usage
+		"\x56\xAB\x22\xBE\xE6\x3D\x82\xD7\xBC\x52\x27\xF6\x77\x3F\x8E\xA7"
+		"\xA5\xEB\x1C\x82\x51\x60\xC3\x83\x12\x98\x0C\x44\x2E\x5C\x7E\x49" // Ke
+		"\x69\xB1\x65\x14\xE3\xCD\x8E\x56\xB8\x20\x10\xD5\xC7\x30\x12\xB6"
+		"\x22\xC4\xD0\x0F\xFC\x23\xED\x1F", // Ki
+		.klen	= 4 + 4 + 32 + 24,
+		.ptext	=
+		"\x76\x3E\x65\x36\x7E\x86\x4F\x02\xF5\x51\x53\xC7\xE3\xB5\x8A\xF1" // Confounder
+		"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
+		"\x10\x11\x12\x13\x14", // Plain
+		.plen	= 16 + 21,
+		.ctext	=
+		"\x40\x01\x3E\x2D\xF5\x8E\x87\x51\x95\x7D\x28\x78\xBC\xD2\xD6\xFE"
+		"\x10\x1C\xCF\xD5\x56\xCB\x1E\xAE\x79\xDB\x3C\x3E\xE8\x64\x29\xF2"
+		"\xB2\xA6\x02\xAC\x86\xFE\xF6\xEC\xB6\x47\xD6\x29\x5F\xAE\x07\x7A"
+		"\x1F\xEB\x51\x75\x08\xD2\xC1\x6B\x41\x92\xE0\x1F\x62",
+		.clen	= 16 + 21 + 24,
+		.setauthsize_error = -EINVAL,
+	}, {
+		// "mic"
+		.key	=
+		"\x00\x00\x00\x02" // KR5_ENCRYPT_MODE_KC
+		"\x00\x00\x00\x00" // Usage
+		"\xEF\x57\x18\xBE\x86\xCC\x84\x96\x3D\x8B\xBB\x50\x31\xE9\xF5\xC4"
+		"\xBA\x41\xF2\x8F\xAF\x69\xE7\x3D", // Kc
+		.klen	= 4 + 4 + 24,
+		.ptext	=
+		"\xAA\x55\xAA\x55\xAA\x55\xAA\x55\xAA\x55\xAA\x55\xAA\x55\xAA\x55"
+		"\xAA\x55\xAA\x55\xAA\x55\xAA\x55" // MIC
+		"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
+		"\x10\x11\x12\x13\x14",
+		.plen	= 24 + 21,
+		.ctext	=
+		"\x45\xEE\x79\x15\x67\xEE\xFC\xA3\x7F\x4A\xC1\xE0\x22\x2D\xE8\x0D"
+		"\x43\xC3\xBF\xA0\x66\x99\x67\x2A" // MIC
+		"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
+		"\x10\x11\x12\x13\x14",
+		.clen	= 24 + 21,
+		.setauthsize_error = -EINVAL,
+	}
+};
+
+static const struct aead_testvec krb5_test_camellia128_cts_cmac[] = {
+	/* rfc6803 sec 10 */
+	{
+		// "enc no plain"
+		.key	=
+		"\x00\x00\x00\x01" // KRB5_ENCRYPT_MODE
+		"\x00\x00\x00\x00" // Usage
+		"\x1D\xC4\x6A\x8D\x76\x3F\x4F\x93\x74\x2B\xCB\xA3\x38\x75\x76\xC3", // K0
+		.klen	= 4 + 4 + 16,
+		.ptext	=
+		"\xB6\x98\x22\xA1\x9A\x6B\x09\xC0\xEB\xC8\x55\x7D\x1F\x1B\x6C\x0A" // Confounder
+		"", // Plain
+		.plen	= 16 + 0,
+		.ctext	=
+		"\xC4\x66\xF1\x87\x10\x69\x92\x1E\xDB\x7C\x6F\xDE\x24\x4A\x52\xDB"
+		"\x0B\xA1\x0E\xDC\x19\x7B\xDB\x80\x06\x65\x8C\xA3\xCC\xCE\x6E\xB8",
+		.clen	= 16 + 0 + 16,
+		.setauthsize_error = -EINVAL,
+	}, {
+		// "enc 1 plain",
+		.key	=
+		"\x00\x00\x00\x01" // KRB5_ENCRYPT_MODE
+		"\x00\x00\x00\x01" // Usage
+		"\x50\x27\xBC\x23\x1D\x0F\x3A\x9D\x23\x33\x3F\x1C\xA6\xFD\xBE\x7C", // K0
+		.klen	= 4 + 4 + 16,
+		.ptext	=
+		"\x6F\x2F\xC3\xC2\xA1\x66\xFD\x88\x98\x96\x7A\x83\xDE\x95\x96\xD9" // Confounder
+		"1", // Plain
+		.plen	= 16 + 1,
+		.ctext	=
+		"\x84\x2D\x21\xFD\x95\x03\x11\xC0\xDD\x46\x4A\x3F\x4B\xE8\xD6\xDA"
+		"\x88\xA5\x6D\x55\x9C\x9B\x47\xD3\xF9\xA8\x50\x67\xAF\x66\x15\x59"
+		"\xB8",
+		.clen	= 16 + 1 + 16,
+		.setauthsize_error = -EINVAL,
+	}, {
+		// "enc 9 plain",
+		.key	=
+		"\x00\x00\x00\x01" // KRB5_ENCRYPT_MODE
+		"\x00\x00\x00\x02" // Usage
+		"\xA1\xBB\x61\xE8\x05\xF9\xBA\x6D\xDE\x8F\xDB\xDD\xC0\x5C\xDE\xA0", // K0
+		.klen	= 4 + 4 + 16,
+		.ptext	=
+		"\xA5\xB4\xA7\x1E\x07\x7A\xEE\xF9\x3C\x87\x63\xC1\x8F\xDB\x1F\x10" // Confounder
+		"9 bytesss", // Plain
+		.plen	= 16 + 9,
+		.ctext	=
+		"\x61\x9F\xF0\x72\xE3\x62\x86\xFF\x0A\x28\xDE\xB3\xA3\x52\xEC\x0D"
+		"\x0E\xDF\x5C\x51\x60\xD6\x63\xC9\x01\x75\x8C\xCF\x9D\x1E\xD3\x3D"
+		"\x71\xDB\x8F\x23\xAA\xBF\x83\x48\xA0",
+		.clen	= 16 + 9 + 16,
+		.setauthsize_error = -EINVAL,
+	}, {
+		// "enc 13 plain",
+		.key	=
+		"\x00\x00\x00\x01" // KRB5_ENCRYPT_MODE
+		"\x00\x00\x00\x03" // Usage
+		"\x2C\xA2\x7A\x5F\xAF\x55\x32\x24\x45\x06\x43\x4E\x1C\xEF\x66\x76", // K0
+		.klen	= 4 + 4 + 16,
+		.ptext	=
+		"\x19\xFE\xE4\x0D\x81\x0C\x52\x4B\x5B\x22\xF0\x18\x74\xC6\x93\xDA" // Confounder
+		"13 bytes byte", // Plain
+		.plen	= 16 + 13,
+		.ctext	=
+		"\xB8\xEC\xA3\x16\x7A\xE6\x31\x55\x12\xE5\x9F\x98\xA7\xC5\x00\x20"
+		"\x5E\x5F\x63\xFF\x3B\xB3\x89\xAF\x1C\x41\xA2\x1D\x64\x0D\x86\x15"
+		"\xC9\xED\x3F\xBE\xB0\x5A\xB6\xAC\xB6\x76\x89\xB5\xEA",
+		.clen	= 16 + 13 + 16,
+		.setauthsize_error = -EINVAL,
+	}, {
+		// "enc 30 plain",
+		.key	=
+		"\x00\x00\x00\x01" // KRB5_ENCRYPT_MODE
+		"\x00\x00\x00\x04" // Usage
+		"\x78\x24\xF8\xC1\x6F\x83\xFF\x35\x4C\x6B\xF7\x51\x5B\x97\x3F\x43", // K0
+		.klen	= 4 + 4 + 16,
+		.ptext	=
+		"\xCA\x7A\x7A\xB4\xBE\x19\x2D\xAB\xD6\x03\x50\x6D\xB1\x9C\x39\xE2" // Confounder
+		"30 bytes bytes bytes bytes byt", // Plain
+		.plen	= 16 + 30,
+		.ctext	=
+		"\xA2\x6A\x39\x05\xA4\xFF\xD5\x81\x6B\x7B\x1E\x27\x38\x0D\x08\x09"
+		"\x0C\x8E\xC1\xF3\x04\x49\x6E\x1A\xBD\xCD\x2B\xDC\xD1\xDF\xFC\x66"
+		"\x09\x89\xE1\x17\xA7\x13\xDD\xBB\x57\xA4\x14\x6C\x15\x87\xCB\xA4"
+		"\x35\x66\x65\x59\x1D\x22\x40\x28\x2F\x58\x42\xB1\x05\xA5",
+		.clen	= 16 + 30 + 16,
+		.setauthsize_error = -EINVAL,
+	}, {
+		// "mic abc"
+		.key	=
+		"\x00\x00\x00\x00" // KR5_ENCRYPT_MODE
+		"\x00\x00\x00\x07" // Usage
+		"\x1D\xC4\x6A\x8D\x76\x3F\x4F\x93\x74\x2B\xCB\xA3\x38\x75\x76\xC3", // K0
+		.klen	= 4 + 4 + 16,
+		.ptext	=
+		"\xAA\x55\xAA\x55\xAA\x55\xAA\x55\xAA\x55\xAA\x55\xAA\x55\xAA\x55" // MIC
+		"abcdefghijk", // Plain
+		.plen	= 16 + 10,
+		.ctext	=
+		"\x11\x78\xE6\xC5\xC4\x7A\x8C\x1A\xE0\xC4\xB9\xC7\xD4\xEB\x7B\x6B" // MIC
+		"abcdefghijk", // Plain
+		.clen	= 16 + 10,
+		.setauthsize_error = -EINVAL,
+	}, {
+		// "mic ABC"
+		.key	=
+		"\x00\x00\x00\x00" // KR5_ENCRYPT_MODE
+		"\x00\x00\x00\x07" // Usage
+		"\x50\x27\xBC\x23\x1D\x0F\x3A\x9D\x23\x33\x3F\x1C\xA6\xFD\xBE\x7C", // K0
+		.klen	= 4 + 4 + 16,
+		.ptext	=
+		"\xAA\x55\xAA\x55\xAA\x55\xAA\x55\xAA\x55\xAA\x55\xAA\x55\xAA\x55" // MIC
+		"ABCDEFGHIJKLMNOPQRSTUVWXYZ", // Plain
+		.plen	= 16 + 26,
+		.ctext	=
+		"\xD1\xB3\x4F\x70\x04\xA7\x31\xF2\x3A\x0C\x00\xBF\x6C\x3F\x75\x3A" // MIC
+		"ABCDEFGHIJKLMNOPQRSTUVWXYZ", // Plain
+		.clen	= 16 + 26,
+		.setauthsize_error = -EINVAL,
+	}
+};
+
+static const struct aead_testvec krb5_test_camellia256_cts_cmac[] = {
+	/* rfc6803 sec 10 */
+	{
+		// "enc no plain",
+		.key	=
+		"\x00\x00\x00\x01" // KRB5_ENCRYPT_MODE
+		"\x00\x00\x00\x00" // Usage
+		"\xB6\x1C\x86\xCC\x4E\x5D\x27\x57\x54\x5A\xD4\x23\x39\x9F\xB7\x03"
+		"\x1E\xCA\xB9\x13\xCB\xB9\x00\xBD\x7A\x3C\x6D\xD8\xBF\x92\x01\x5B", // K0
+		.klen	= 4 + 4 + 32,
+		.ptext	=
+		"\x3C\xBB\xD2\xB4\x59\x17\x94\x10\x67\xF9\x65\x99\xBB\x98\x92\x6C" // Confounder
+		"", // Plain
+		.plen	= 16 + 1,
+		.ctext	=
+		"\x03\x88\x6D\x03\x31\x0B\x47\xA6\xD8\xF0\x6D\x7B\x94\xD1\xDD\x83"
+		"\x7E\xCC\xE3\x15\xEF\x65\x2A\xFF\x62\x08\x59\xD9\x4A\x25\x92\x66",
+		.clen	= 16 + 0 + 16,
+		.setauthsize_error = -EINVAL,
+	}, {
+		// "enc 1 plain",
+		.key	=
+		"\x00\x00\x00\x01" // KRB5_ENCRYPT_MODE
+		"\x00\x00\x00\x01" // Usage
+		"\x1B\x97\xFE\x0A\x19\x0E\x20\x21\xEB\x30\x75\x3E\x1B\x6E\x1E\x77"
+		"\xB0\x75\x4B\x1D\x68\x46\x10\x35\x58\x64\x10\x49\x63\x46\x38\x33", // K0
+		.klen	= 4 + 4 + 32,
+		.ptext	=
+		"\xDE\xF4\x87\xFC\xEB\xE6\xDE\x63\x46\xD4\xDA\x45\x21\xBB\xA2\xD2" // Confounder
+		"1", // Plain
+		.plen	= 16 + 1,
+		.ctext	=
+		"\x2C\x9C\x15\x70\x13\x3C\x99\xBF\x6A\x34\xBC\x1B\x02\x12\x00\x2F"
+		"\xD1\x94\x33\x87\x49\xDB\x41\x35\x49\x7A\x34\x7C\xFC\xD9\xD1\x8A"
+		"\x12",
+		.clen	= 16 + 1 + 16,
+		.setauthsize_error = -EINVAL,
+	}, {
+		// "enc 9 plain",
+		.key	=
+		"\x00\x00\x00\x01" // KRB5_ENCRYPT_MODE
+		"\x00\x00\x00\x02" // Usage
+		"\x32\x16\x4C\x5B\x43\x4D\x1D\x15\x38\xE4\xCF\xD9\xBE\x80\x40\xFE"
+		"\x8C\x4A\xC7\xAC\xC4\xB9\x3D\x33\x14\xD2\x13\x36\x68\x14\x7A\x05", // K0
+		.klen	= 4 + 4 + 32,
+		.ptext	=
+		"\xAD\x4F\xF9\x04\xD3\x4E\x55\x53\x84\xB1\x41\x00\xFC\x46\x5F\x88" // Confounder
+		"9 bytesss", // Plain
+		.plen	= 16 + 9,
+		.ctext	=
+		"\x9C\x6D\xE7\x5F\x81\x2D\xE7\xED\x0D\x28\xB2\x96\x35\x57\xA1\x15"
+		"\x64\x09\x98\x27\x5B\x0A\xF5\x15\x27\x09\x91\x3F\xF5\x2A\x2A\x9C"
+		"\x8E\x63\xB8\x72\xF9\x2E\x64\xC8\x39",
+		.clen	= 16 + 9 + 16,
+		.setauthsize_error = -EINVAL,
+	}, {
+		// "enc 13 plain",
+		.key	=
+		"\x00\x00\x00\x01" // KRB5_ENCRYPT_MODE
+		"\x00\x00\x00\x03" // Usage
+		"\xB0\x38\xB1\x32\xCD\x8E\x06\x61\x22\x67\xFA\xB7\x17\x00\x66\xD8"
+		"\x8A\xEC\xCB\xA0\xB7\x44\xBF\xC6\x0D\xC8\x9B\xCA\x18\x2D\x07\x15", // K0
+		.klen	= 4 + 4 + 32,
+		.ptext	=
+		"\xCF\x9B\xCA\x6D\xF1\x14\x4E\x0C\x0A\xF9\xB8\xF3\x4C\x90\xD5\x14" // Confounder
+		"13 bytes byte",
+		.plen	= 16 + 1,
+		.ctext	=
+		"\xEE\xEC\x85\xA9\x81\x3C\xDC\x53\x67\x72\xAB\x9B\x42\xDE\xFC\x57"
+		"\x06\xF7\x26\xE9\x75\xDD\xE0\x5A\x87\xEB\x54\x06\xEA\x32\x4C\xA1"
+		"\x85\xC9\x98\x6B\x42\xAA\xBE\x79\x4B\x84\x82\x1B\xEE",
+		.clen	= 16 + 0 + 16,
+		.setauthsize_error = -EINVAL,
+	}, {
+		// "enc 30 plain",
+		.key	=
+		"\x00\x00\x00\x01" // KRB5_ENCRYPT_MODE
+		"\x00\x00\x00\x04" // Usage
+		"\xCC\xFC\xD3\x49\xBF\x4C\x66\x77\xE8\x6E\x4B\x02\xB8\xEA\xB9\x24"
+		"\xA5\x46\xAC\x73\x1C\xF9\xBF\x69\x89\xB9\x96\xE7\xD6\xBF\xBB\xA7", // K0
+		.klen	= 4 + 4 + 32,
+		.ptext	=
+		"\x64\x4D\xEF\x38\xDA\x35\x00\x72\x75\x87\x8D\x21\x68\x55\xE2\x28" // Confounder
+		"30 bytes bytes bytes bytes byt", // Plain
+		.plen	= 16 + 30,
+		.ctext	=
+		"\x0E\x44\x68\x09\x85\x85\x5F\x2D\x1F\x18\x12\x52\x9C\xA8\x3B\xFD"
+		"\x8E\x34\x9D\xE6\xFD\x9A\xDA\x0B\xAA\xA0\x48\xD6\x8E\x26\x5F\xEB"
+		"\xF3\x4A\xD1\x25\x5A\x34\x49\x99\xAD\x37\x14\x68\x87\xA6\xC6\x84"
+		"\x57\x31\xAC\x7F\x46\x37\x6A\x05\x04\xCD\x06\x57\x14\x74",
+		.clen	= 16 + 30 + 16,
+		.setauthsize_error = -EINVAL,
+	}, {
+		// "mic 123"
+		.key	=
+		"\x00\x00\x00\x00" // KR5_ENCRYPT_MODE
+		"\x00\x00\x00\x09" // Usage
+		"\xB6\x1C\x86\xCC\x4E\x5D\x27\x57\x54\x5A\xD4\x23\x39\x9F\xB7\x03"
+		"\x1E\xCA\xB9\x13\xCB\xB9\x00\xBD\x7A\x3C\x6D\xD8\xBF\x92\x01\x5B", // K0
+		.klen	= 4 + 4 + 32,
+		.ptext	=
+		"\xAA\x55\xAA\x55\xAA\x55\xAA\x55\xAA\x55\xAA\x55\xAA\x55\xAA\x55" // MIC
+		"123456789", // Plain
+		.plen	= 16 + 9,
+		.ctext	=
+		"\x87\xA1\x2C\xFD\x2B\x96\x21\x48\x10\xF0\x1C\x82\x6E\x77\x44\xB1" // MIC
+		"123456789", // Plain
+		.clen	= 16 + 9,
+		.setauthsize_error = -EINVAL,
+	}, {
+		// "mic !@#"
+		.key	=
+		"\x00\x00\x00\x00" // KR5_ENCRYPT_MODE
+		"\x00\x00\x00\x0a" // Usage
+		"\x32\x16\x4C\x5B\x43\x4D\x1D\x15\x38\xE4\xCF\xD9\xBE\x80\x40\xFE"
+		"\x8C\x4A\xC7\xAC\xC4\xB9\x3D\x33\x14\xD2\x13\x36\x68\x14\x7A\x05", // K0
+		.klen	= 4 + 4 + 32,
+		.ptext	=
+		"\xAA\x55\xAA\x55\xAA\x55\xAA\x55\xAA\x55\xAA\x55\xAA\x55\xAA\x55" // MIC
+		"!@#$%^&*()!@#$%^&*()!@#$%^&*()", // Plain
+		.plen	= 16 + 30,
+		.ctext	=
+		"\x3F\xA0\xB4\x23\x55\xE5\x2B\x18\x91\x87\x29\x4A\xA2\x52\xAB\x64" // MIC
+		"!@#$%^&*()!@#$%^&*()!@#$%^&*()", // Plain
+		.clen	= 16 + 30,
+		.setauthsize_error = -EINVAL,
+	}
+};
+
 #endif	/* _CRYPTO_TESTMGR_H */
diff --git a/include/crypto/aead.h b/include/crypto/aead.h
index 0e8a41638678..0b0826131883 100644
--- a/include/crypto/aead.h
+++ b/include/crypto/aead.h
@@ -121,6 +121,7 @@ struct aead_request {
  * @decrypt: see struct skcipher_alg
  * @ivsize: see struct skcipher_alg
  * @chunksize: see struct skcipher_alg
+ * @no_authtags: This algo doesn't use authentication tags.
  * @init: Initialize the cryptographic transformation object. This function
  *	  is used to initialize the cryptographic transformation object.
  *	  This function is called only once at the instantiation time, right
@@ -148,6 +149,7 @@ struct aead_alg {
 	unsigned int ivsize;
 	unsigned int maxauthsize;
 	unsigned int chunksize;
+	bool no_authtags;
 
 	struct crypto_alg base;
 };



From dhowells at redhat.com  Thu Jan  9 17:03:06 2025
From: dhowells at redhat.com (David Howells)
Date: Fri, 10 Jan 2025 01:03:06 +0000
Subject: [RFC PATCH 4/8] rxrpc: Add the security index for yfs-rxgk
In-Reply-To: <20250110010313.1471063-1-dhowells@redhat.com>
References: <20250110010313.1471063-1-dhowells@redhat.com>
Message-ID: <20250110010313.1471063-5-dhowells@redhat.com>

Add the security index and abort codes for the YFS variant of rxgk.

Signed-off-by: David Howells <dhowells at redhat.com>
cc: Herbert Xu <herbert at gondor.apana.org.au>
cc: "David S. Miller" <davem at davemloft.net>
cc: Chuck Lever <chuck.lever at oracle.com>
cc: Marc Dionne <marc.dionne at auristor.com>
cc: Eric Dumazet <edumazet at google.com>
cc: Jakub Kicinski <kuba at kernel.org>
cc: Paolo Abeni <pabeni at redhat.com>
cc: Simon Horman <horms at kernel.org>
cc: linux-afs at lists.infradead.org
cc: linux-nfs at vger.kernel.org
cc: linux-crypto at vger.kernel.org
cc: netdev at vger.kernel.org
---
 fs/afs/misc.c              | 13 +++++++++++++
 include/uapi/linux/rxrpc.h | 17 +++++++++++++++++
 2 files changed, 30 insertions(+)

diff --git a/fs/afs/misc.c b/fs/afs/misc.c
index b8180bf2281f..57f779804d50 100644
--- a/fs/afs/misc.c
+++ b/fs/afs/misc.c
@@ -103,6 +103,19 @@ int afs_abort_to_error(u32 abort_code)
 	case RXKADDATALEN:	return -EKEYREJECTED;
 	case RXKADILLEGALLEVEL:	return -EKEYREJECTED;
 
+	case RXGK_INCONSISTENCY:	return -EPROTO;
+	case RXGK_PACKETSHORT:		return -EPROTO;
+	case RXGK_BADCHALLENGE:		return -EPROTO;
+	case RXGK_BADETYPE:		return -ENOPKG;
+	case RXGK_BADLEVEL:		return -EKEYREJECTED;
+	case RXGK_BADKEYNO:		return -EKEYREJECTED;
+	case RXGK_EXPIRED:		return -EKEYEXPIRED;
+	case RXGK_NOTAUTH:		return -EKEYREJECTED;
+	case RXGK_BAD_TOKEN:		return -EKEYREJECTED;
+	case RXGK_SEALED_INCON:		return -EKEYREJECTED;
+	case RXGK_DATA_LEN:		return -EPROTO;
+	case RXGK_BAD_QOP:		return -EKEYREJECTED;
+
 	case RXGEN_OPCODE:	return -ENOTSUPP;
 
 	default:		return -EREMOTEIO;
diff --git a/include/uapi/linux/rxrpc.h b/include/uapi/linux/rxrpc.h
index 8f8dc7a937a4..0e296d219191 100644
--- a/include/uapi/linux/rxrpc.h
+++ b/include/uapi/linux/rxrpc.h
@@ -73,6 +73,7 @@ enum rxrpc_cmsg_type {
 #define RXRPC_SECURITY_RXKAD	2	/* kaserver or kerberos 4 */
 #define RXRPC_SECURITY_RXGK	4	/* gssapi-based */
 #define RXRPC_SECURITY_RXK5	5	/* kerberos 5 */
+#define RXRPC_SECURITY_YFS_RXGK	6	/* YFS gssapi-based */
 
 /*
  * RxRPC-level abort codes
@@ -118,4 +119,20 @@ enum rxrpc_cmsg_type {
 #define RXKADDATALEN		19270411	/* user data too long */
 #define RXKADILLEGALLEVEL	19270412	/* caller not authorised to use encrypted conns */
 
+/*
+ * RxGK GSSAPI security abort codes.
+ */
+#define RXGK_INCONSISTENCY	1233242880	/* Security module structure inconsistent */
+#define RXGK_PACKETSHORT	1233242881	/* Packet too short for security challenge */
+#define RXGK_BADCHALLENGE	1233242882	/* Invalid security challenge */
+#define RXGK_BADETYPE		1233242883	/* Invalid or impermissible encryption type */
+#define RXGK_BADLEVEL		1233242884	/* Invalid or impermissible security level */
+#define RXGK_BADKEYNO		1233242885	/* Key version number not found */
+#define RXGK_EXPIRED		1233242886	/* Token has expired */
+#define RXGK_NOTAUTH		1233242887	/* Caller not authorized */
+#define RXGK_BAD_TOKEN		1233242888	/* Security object was passed a bad token */
+#define RXGK_SEALED_INCON	1233242889	/* Sealed data inconsistent */
+#define RXGK_DATA_LEN		1233242890	/* User data too long */
+#define RXGK_BAD_QOP		1233242891	/* Inadequate quality of protection available */
+
 #endif /* _UAPI_LINUX_RXRPC_H */



From dhowells at redhat.com  Thu Jan  9 17:03:07 2025
From: dhowells at redhat.com (David Howells)
Date: Fri, 10 Jan 2025 01:03:07 +0000
Subject: [RFC PATCH 5/8] rxrpc: Add YFS RxGK (GSSAPI) security class
In-Reply-To: <20250110010313.1471063-1-dhowells@redhat.com>
References: <20250110010313.1471063-1-dhowells@redhat.com>
Message-ID: <20250110010313.1471063-6-dhowells@redhat.com>

Add support for the YFS-variant RxGK security class to support
GSSAPI-derived authentication.  This also allows the use of better crypto
over the rxkad security class.

The key payload is XDR encoded of the form:

    typedef int64_t opr_time;

    const AFSTOKEN_RK_TIX_MAX = 12000; 	/* Matches entry in rxkad.h */

    struct token_rxkad {
	afs_int32 viceid;
	afs_int32 kvno;
	afs_int64 key;
	afs_int32 begintime;
	afs_int32 endtime;
	afs_int32 primary_flag;
	opaque ticket<AFSTOKEN_RK_TIX_MAX>;
    };

    struct token_rxgk {
	opr_time begintime;
	opr_time endtime;
	afs_int64 level;
	afs_int64 lifetime;
	afs_int64 bytelife;
	afs_int64 enctype;
	opaque key<>;
	opaque ticket<>;
    };

    const AFSTOKEN_UNION_NOAUTH = 0;
    const AFSTOKEN_UNION_KAD = 2;
    const AFSTOKEN_UNION_YFSGK = 6;

    union ktc_tokenUnion switch (afs_int32 type) {
	case AFSTOKEN_UNION_KAD:
	    token_rxkad kad;
	case AFSTOKEN_UNION_YFSGK:
	    token_rxgk  gk;
    };

    const AFSTOKEN_LENGTH_MAX = 16384;
    typedef opaque token_opaque<AFSTOKEN_LENGTH_MAX>;

    const AFSTOKEN_MAX = 8;
    const AFSTOKEN_CELL_MAX = 64;

    struct ktc_setTokenData {
	afs_int32 flags;
	string cell<AFSTOKEN_CELL_MAX>;
	token_opaque tokens<AFSTOKEN_MAX>;
    };

The parser for the basic token struct is already present, as is the rxkad
token type.  This adds a parser for the rxgk token type.

Signed-off-by: David Howells <dhowells at redhat.com>
cc: Herbert Xu <herbert at gondor.apana.org.au>
cc: "David S. Miller" <davem at davemloft.net>
cc: Chuck Lever <chuck.lever at oracle.com>
cc: Marc Dionne <marc.dionne at auristor.com>
cc: Eric Dumazet <edumazet at google.com>
cc: Jakub Kicinski <kuba at kernel.org>
cc: Paolo Abeni <pabeni at redhat.com>
cc: Simon Horman <horms at kernel.org>
cc: linux-afs at lists.infradead.org
cc: linux-nfs at vger.kernel.org
cc: linux-crypto at vger.kernel.org
cc: netdev at vger.kernel.org
---
 include/keys/rxrpc-type.h |  17 ++++
 net/rxrpc/key.c           | 183 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 200 insertions(+)

diff --git a/include/keys/rxrpc-type.h b/include/keys/rxrpc-type.h
index 333c0f49a9cd..0ddbe197a261 100644
--- a/include/keys/rxrpc-type.h
+++ b/include/keys/rxrpc-type.h
@@ -9,6 +9,7 @@
 #define _KEYS_RXRPC_TYPE_H
 
 #include <linux/key.h>
+#include <crypto/krb5.h>
 
 /*
  * key type for AF_RXRPC keys
@@ -31,6 +32,21 @@ struct rxkad_key {
 	u8	ticket[];		/* the encrypted ticket */
 };
 
+/*
+ * RxRPC key for YFS-RxGK (type-6 security)
+ */
+struct rxgk_key {
+	s64		begintime;	/* Time at which the ticket starts */
+	s64		endtime;	/* Time at which the ticket ends */
+	u64		lifetime;	/* Maximum lifespan of a connection (seconds) */
+	u64		bytelife;	/* Maximum number of bytes on a connection */
+	unsigned int	enctype;	/* Encoding type */
+	s8		level;		/* Negotiated security RXRPC_SECURITY_PLAIN/AUTH/ENCRYPT */
+	struct krb5_buffer key;		/* Master key, K0 */
+	struct krb5_buffer ticket;	/* Ticket to be passed to server */
+	u8		_key[];		/* Key storage */
+};
+
 /*
  * list of tokens attached to an rxrpc key
  */
@@ -40,6 +56,7 @@ struct rxrpc_key_token {
 	struct rxrpc_key_token *next;	/* the next token in the list */
 	union {
 		struct rxkad_key *kad;
+		struct rxgk_key *rxgk;
 	};
 };
 
diff --git a/net/rxrpc/key.c b/net/rxrpc/key.c
index 33e8302a79e3..2e42a0bbeac2 100644
--- a/net/rxrpc/key.c
+++ b/net/rxrpc/key.c
@@ -129,6 +129,158 @@ static int rxrpc_preparse_xdr_rxkad(struct key_preparsed_payload *prep,
 	return 0;
 }
 
+static u64 xdr_dec64(const __be32 *xdr)
+{
+	return (u64)ntohl(xdr[0]) << 32 | (u64)ntohl(xdr[1]);
+}
+
+static time64_t rxrpc_s64_to_time64(s64 time_in_100ns)
+{
+	bool neg = false;
+	u64 tmp = time_in_100ns;
+
+	if (time_in_100ns < 0) {
+		tmp = -time_in_100ns;
+		neg = true;
+	}
+	do_div(tmp, 10000000);
+	return neg ? -tmp : tmp;
+}
+
+/*
+ * Parse a YFS-RxGK type XDR format token
+ * - the caller guarantees we have at least 4 words
+ *
+ * struct token_rxgk {
+ *	opr_time begintime;
+ *	opr_time endtime;
+ *	afs_int64 level;
+ *	afs_int64 lifetime;
+ *	afs_int64 bytelife;
+ *	afs_int64 enctype;
+ *	opaque key<>;
+ *	opaque ticket<>;
+ * };
+ */
+static int rxrpc_preparse_xdr_yfs_rxgk(struct key_preparsed_payload *prep,
+				       size_t datalen,
+				       const __be32 *xdr, unsigned int toklen)
+{
+	struct rxrpc_key_token *token, **pptoken;
+	time64_t expiry;
+	size_t plen;
+	const __be32 *ticket, *key;
+	s64 tmp;
+	u32 tktlen, keylen;
+
+	_enter(",{%x,%x,%x,%x},%x",
+	       ntohl(xdr[0]), ntohl(xdr[1]), ntohl(xdr[2]), ntohl(xdr[3]),
+	       toklen);
+
+	if (6 * 2 + 2 > toklen / 4)
+		goto reject;
+
+	key = xdr + (6 * 2 + 1);
+	keylen = ntohl(key[-1]);
+	_debug("keylen: %x", keylen);
+	keylen = round_up(keylen, 4);
+	if ((6 * 2 + 2) * 4 + keylen > toklen)
+		goto reject;
+
+	ticket = xdr + (6 * 2 + 1 + (keylen / 4) + 1);
+	tktlen = ntohl(ticket[-1]);
+	_debug("tktlen: %x", tktlen);
+	tktlen = round_up(tktlen, 4);
+	if ((6 * 2 + 2) * 4 + keylen + tktlen != toklen) {
+		kleave(" = -EKEYREJECTED [%x!=%x, %x,%x]",
+		       (6 * 2 + 2) * 4 + keylen + tktlen, toklen,
+		       keylen, tktlen);
+		goto reject;
+	}
+
+	plen = sizeof(*token) + sizeof(*token->rxgk) + tktlen + keylen;
+	prep->quotalen = datalen + plen;
+
+	plen -= sizeof(*token);
+	token = kzalloc(sizeof(*token), GFP_KERNEL);
+	if (!token)
+		goto nomem;
+
+	token->rxgk = kzalloc(sizeof(*token->rxgk) + keylen, GFP_KERNEL);
+	if (!token->rxgk)
+		goto nomem_token;
+
+	token->security_index	= RXRPC_SECURITY_YFS_RXGK;
+	token->rxgk->begintime	= xdr_dec64(xdr + 0 * 2);
+	token->rxgk->endtime	= xdr_dec64(xdr + 1 * 2);
+	token->rxgk->level	= tmp = xdr_dec64(xdr + 2 * 2);
+	if (tmp < -1LL || tmp > RXRPC_SECURITY_ENCRYPT)
+		goto reject_token;
+	token->rxgk->lifetime	= xdr_dec64(xdr + 3 * 2);
+	token->rxgk->bytelife	= xdr_dec64(xdr + 4 * 2);
+	token->rxgk->enctype	= tmp = xdr_dec64(xdr + 5 * 2);
+	if (tmp < 0 || tmp > UINT_MAX)
+		goto reject_token;
+	token->rxgk->key.len	= ntohl(key[-1]);
+	token->rxgk->key.data	= token->rxgk->_key;
+	token->rxgk->ticket.len = ntohl(ticket[-1]);
+
+	expiry = rxrpc_s64_to_time64(token->rxgk->endtime);
+	if (expiry < 0)
+		goto expired;
+	if (expiry < prep->expiry)
+		prep->expiry = expiry;
+
+	memcpy(token->rxgk->key.data, key, token->rxgk->key.len);
+
+	/* Pad the ticket so that we can use it directly in XDR */
+	token->rxgk->ticket.data = kzalloc(round_up(token->rxgk->ticket.len, 4),
+					   GFP_KERNEL);
+	if (!token->rxgk->ticket.data)
+		goto nomem_yrxgk;
+	memcpy(token->rxgk->ticket.data, ticket, token->rxgk->ticket.len);
+
+	_debug("SCIX: %u",	token->security_index);
+	_debug("EXPY: %llx",	token->rxgk->endtime);
+	_debug("LIFE: %llx",	token->rxgk->lifetime);
+	_debug("BYTE: %llx",	token->rxgk->bytelife);
+	_debug("ENC : %u",	token->rxgk->enctype);
+	_debug("LEVL: %u",	token->rxgk->level);
+	_debug("KLEN: %u",	token->rxgk->key.len);
+	_debug("TLEN: %u",	token->rxgk->ticket.len);
+	_debug("KEY0: %*phN",	token->rxgk->key.len, token->rxgk->key.data);
+	_debug("TICK: %*phN",
+	       min_t(u32, token->rxgk->ticket.len, 32), token->rxgk->ticket.data);
+
+	/* count the number of tokens attached */
+	prep->payload.data[1] = (void *)((unsigned long)prep->payload.data[1] + 1);
+
+	/* attach the data */
+	for (pptoken = (struct rxrpc_key_token **)&prep->payload.data[0];
+	     *pptoken;
+	     pptoken = &(*pptoken)->next)
+		continue;
+	*pptoken = token;
+
+	_leave(" = 0");
+	return 0;
+
+nomem_yrxgk:
+	kfree(token->rxgk);
+nomem_token:
+	kfree(token);
+nomem:
+	return -ENOMEM;
+reject_token:
+	kfree(token);
+reject:
+	return -EKEYREJECTED;
+expired:
+	kfree(token->rxgk);
+	kfree(token);
+	return -EKEYEXPIRED;
+}
+
 /*
  * attempt to parse the data as the XDR format
  * - the caller guarantees we have more than 7 words
@@ -228,6 +380,9 @@ static int rxrpc_preparse_xdr(struct key_preparsed_payload *prep)
 		case RXRPC_SECURITY_RXKAD:
 			ret2 = rxrpc_preparse_xdr_rxkad(prep, datalen, token, toklen);
 			break;
+		case RXRPC_SECURITY_YFS_RXGK:
+			ret2 = rxrpc_preparse_xdr_yfs_rxgk(prep, datalen, token, toklen);
+			break;
 		default:
 			ret2 = -EPROTONOSUPPORT;
 			break;
@@ -390,6 +545,10 @@ static void rxrpc_free_token_list(struct rxrpc_key_token *token)
 		case RXRPC_SECURITY_RXKAD:
 			kfree(token->kad);
 			break;
+		case RXRPC_SECURITY_YFS_RXGK:
+			kfree(token->rxgk->ticket.data);
+			kfree(token->rxgk);
+			break;
 		default:
 			pr_err("Unknown token type %x on rxrpc key\n",
 			       token->security_index);
@@ -433,6 +592,9 @@ static void rxrpc_describe(const struct key *key, struct seq_file *m)
 		case RXRPC_SECURITY_RXKAD:
 			seq_puts(m, "ka");
 			break;
+		case RXRPC_SECURITY_YFS_RXGK:
+			seq_puts(m, "ygk");
+			break;
 		default: /* we have a ticket we can't encode */
 			seq_printf(m, "%u", token->security_index);
 			break;
@@ -595,6 +757,13 @@ static long rxrpc_read(const struct key *key,
 				toksize += RND(token->kad->ticket_len);
 			break;
 
+		case RXRPC_SECURITY_YFS_RXGK:
+			toksize += 6 * 8 + 2 * 4;
+			if (!token->no_leak_key)
+				toksize += RND(token->rxgk->key.len);
+			toksize += RND(token->rxgk->ticket.len);
+			break;
+
 		default: /* we have a ticket we can't encode */
 			pr_err("Unsupported key token type (%u)\n",
 			       token->security_index);
@@ -674,6 +843,20 @@ static long rxrpc_read(const struct key *key,
 				ENCODE_DATA(token->kad->ticket_len, token->kad->ticket);
 			break;
 
+		case RXRPC_SECURITY_YFS_RXGK:
+			ENCODE64(token->rxgk->begintime);
+			ENCODE64(token->rxgk->endtime);
+			ENCODE64(token->rxgk->level);
+			ENCODE64(token->rxgk->lifetime);
+			ENCODE64(token->rxgk->bytelife);
+			ENCODE64(token->rxgk->enctype);
+			if (token->no_leak_key)
+				ENCODE(0);
+			else
+				ENCODE_DATA(token->rxgk->key.len, token->rxgk->key.data);
+			ENCODE_DATA(token->rxgk->ticket.len, token->rxgk->ticket.data);
+			break;
+
 		default:
 			pr_err("Unsupported key token type (%u)\n",
 			       token->security_index);



From dhowells at redhat.com  Thu Jan  9 17:03:08 2025
From: dhowells at redhat.com (David Howells)
Date: Fri, 10 Jan 2025 01:03:08 +0000
Subject: [RFC PATCH 6/8] rxrpc: rxgk: Provide infrastructure and key derivation
In-Reply-To: <20250110010313.1471063-1-dhowells@redhat.com>
References: <20250110010313.1471063-1-dhowells@redhat.com>
Message-ID: <20250110010313.1471063-7-dhowells@redhat.com>

Provide some infrastructure for implementing the RxGK transport security
class:

 (1) A definition of an encoding type, including:

	- Relevant crypto-layer names
	- Lengths of the crypto keys and checksums involved
	- Crypto functions specific to the encoding type
	- Crypto scheme used for that type

 (2) A definition of a crypto scheme, including:

	- Underlying crypto handlers
	- The pseudo-random function, PRF, used in base key derivation
	- Functions for deriving usage keys Kc, Ke and Ki
	- Functions for en/decrypting parts of an sk_buff

 (3) A key context, with the usage keys required for a derivative of a
     transport key for a specific key number.  This includes keys for
     securing packets for transmission, extracting received packets and
     dealing with response packets.

 (3) A function to look up an encoding type by number.

 (4) A function to set up a key context and derive the keys.

 (5) A function to set up the keys required to extract the ticket obtained
     from the GSS negotiation in the server.

 (6) Miscellaneous functions for context handling.

The keys and key derivation functions are described in:

	tools.ietf.org/html/draft-wilkinson-afs3-rxgk-11

Signed-off-by: David Howells <dhowells at redhat.com>
cc: Herbert Xu <herbert at gondor.apana.org.au>
cc: "David S. Miller" <davem at davemloft.net>
cc: Chuck Lever <chuck.lever at oracle.com>
cc: Marc Dionne <marc.dionne at auristor.com>
cc: Eric Dumazet <edumazet at google.com>
cc: Jakub Kicinski <kuba at kernel.org>
cc: Paolo Abeni <pabeni at redhat.com>
cc: Simon Horman <horms at kernel.org>
cc: linux-afs at lists.infradead.org
cc: linux-nfs at vger.kernel.org
cc: linux-crypto at vger.kernel.org
cc: netdev at vger.kernel.org
---
 net/rxrpc/Kconfig       |  10 ++
 net/rxrpc/Makefile      |   3 +-
 net/rxrpc/ar-internal.h |   3 +
 net/rxrpc/rxgk_common.h |  44 +++++++
 net/rxrpc/rxgk_kdf.c    | 260 ++++++++++++++++++++++++++++++++++++++++
 5 files changed, 319 insertions(+), 1 deletion(-)
 create mode 100644 net/rxrpc/rxgk_common.h
 create mode 100644 net/rxrpc/rxgk_kdf.c

diff --git a/net/rxrpc/Kconfig b/net/rxrpc/Kconfig
index a20986806fea..0a2b38b9b94a 100644
--- a/net/rxrpc/Kconfig
+++ b/net/rxrpc/Kconfig
@@ -67,6 +67,16 @@ config RXKAD
 
 	  See Documentation/networking/rxrpc.rst.
 
+config RXGK
+	bool "RxRPC GSSAPI security"
+	depends on AF_RXRPC
+	depends on CRYPTO_KRB5
+	help
+	  Provide the GSSAPI-based RxGK security class for AFS.  Keys are added
+	  with add_key().
+
+	  See Documentation/networking/rxrpc.rst.
+
 config RXPERF
 	tristate "RxRPC test service"
 	help
diff --git a/net/rxrpc/Makefile b/net/rxrpc/Makefile
index 210b75e3179e..9c8eb1471054 100644
--- a/net/rxrpc/Makefile
+++ b/net/rxrpc/Makefile
@@ -39,6 +39,7 @@ rxrpc-y := \
 rxrpc-$(CONFIG_PROC_FS) += proc.o
 rxrpc-$(CONFIG_RXKAD) += rxkad.o
 rxrpc-$(CONFIG_SYSCTL) += sysctl.o
-
+rxrpc-$(CONFIG_RXGK) += \
+	rxgk_kdf.o
 
 obj-$(CONFIG_RXPERF) += rxperf.o
diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h
index 718193df9d2e..2392f2e062c2 100644
--- a/net/rxrpc/ar-internal.h
+++ b/net/rxrpc/ar-internal.h
@@ -526,6 +526,9 @@ struct rxrpc_connection {
 			struct rxrpc_crypt csum_iv;	/* packet checksum base */
 			u32	nonce;		/* response re-use preventer */
 		} rxkad;
+		struct {
+			u64	start_time;	/* The start time for TK derivation */
+		} rxgk;
 	};
 	unsigned long		flags;
 	unsigned long		events;
diff --git a/net/rxrpc/rxgk_common.h b/net/rxrpc/rxgk_common.h
new file mode 100644
index 000000000000..84e76fe8e324
--- /dev/null
+++ b/net/rxrpc/rxgk_common.h
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* Common bits for GSSAPI-based RxRPC security.
+ *
+ * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells at redhat.com)
+ */
+
+#include <crypto/krb5.h>
+#include <crypto/skcipher.h>
+#include <crypto/hash.h>
+
+/*
+ * Per-key number context.  This is replaced when the connection is rekeyed.
+ */
+struct rxgk_context {
+	refcount_t		usage;
+	unsigned int		key_number;	/* Rekeying number (goes in the rx header) */
+	unsigned long		flags;
+#define RXGK_TK_NEEDS_REKEY	0		/* Set if this needs rekeying */
+	unsigned long		expiry;		/* Expiration time of this key */
+	long long		bytes_remaining; /* Remaining Tx lifetime of this key */
+	const struct krb5_enctype *krb5;	/* RxGK encryption type */
+	const struct rxgk_key	*key;
+
+	/* We need up to 7 keys derived from the transport key, but we don't
+	 * actually need the transport key.  Each key is derived by
+	 * DK(TK,constant).
+	 */
+	struct crypto_aead	*tx_crypto;	/* Transmission key */
+	struct crypto_aead	*rx_crypto;	/* Reception key */
+	struct crypto_aead	*resp_crypto;	/* Response key */
+};
+
+/*
+ * rxgk_kdf.c
+ */
+void rxgk_put(struct rxgk_context *gk);
+struct rxgk_context *rxgk_generate_transport_key(struct rxrpc_connection *conn,
+						 const struct rxgk_key *key,
+						 unsigned int key_number,
+						 gfp_t gfp);
+struct crypto_aead *rxgk_set_up_token_cipher(const struct krb5_buffer *server_key,
+					     unsigned int enctype,
+					     gfp_t gfp);
diff --git a/net/rxrpc/rxgk_kdf.c b/net/rxrpc/rxgk_kdf.c
new file mode 100644
index 000000000000..4257d3d0190b
--- /dev/null
+++ b/net/rxrpc/rxgk_kdf.c
@@ -0,0 +1,260 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* RxGK transport key derivation.
+ *
+ * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells at redhat.com)
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/key-type.h>
+#include <linux/slab.h>
+#include <keys/rxrpc-type.h>
+#include "ar-internal.h"
+#include "rxgk_common.h"
+
+/*
+ * Constants used to derive the keys and hmacs actually used for doing stuff.
+ */
+#define RXGK_CLIENT_ENC_PACKET		1026U // 0x402
+#define RXGK_CLIENT_MIC_PACKET		1027U // 0x403
+#define RXGK_SERVER_ENC_PACKET		1028U // 0x404
+#define RXGK_SERVER_MIC_PACKET		1029U // 0x405
+#define RXGK_CLIENT_ENC_RESPONSE	1030U // 0x406
+#define RXGK_SERVER_ENC_TOKEN		1036U // 0x40c
+
+#define round16(x) (((x) + 15) & ~15)
+
+static void rxgk_free(struct rxgk_context *gk)
+{
+	crypto_free_aead(gk->tx_crypto);
+	crypto_free_aead(gk->rx_crypto);
+	crypto_free_aead(gk->resp_crypto);
+	kfree(gk);
+}
+
+void rxgk_put(struct rxgk_context *gk)
+{
+	if (gk && refcount_dec_and_test(&gk->usage))
+		rxgk_free(gk);
+}
+
+/*
+ * Transport key derivation function.
+ *
+ *      TK = random-to-key(PRF+(K0, L,
+ *                         epoch || cid || start_time || key_number))
+ *      [tools.ietf.org/html/draft-wilkinson-afs3-rxgk-11 sec 8.3]
+ */
+static int rxgk_derive_transport_key(struct rxrpc_connection *conn,
+				     struct rxgk_context *gk,
+				     const struct rxgk_key *rxgk,
+				     struct krb5_buffer *TK,
+				     gfp_t gfp)
+{
+	const struct krb5_enctype *krb5 = gk->krb5;
+	struct krb5_buffer conn_info;
+	unsigned int L = krb5->key_bytes;
+	__be32 *info;
+	u8 *buffer;
+	int ret;
+
+	_enter("");
+
+	conn_info.len = sizeof(__be32) * 5;
+
+	buffer = kzalloc(round16(conn_info.len), gfp);
+	if (!buffer)
+		return -ENOMEM;
+
+	conn_info.data = buffer;
+
+	info = (__be32 *)conn_info.data;
+	info[0] = htonl(conn->proto.epoch);
+	info[1] = htonl(conn->proto.cid);
+	info[2] = htonl(conn->rxgk.start_time >> 32);
+	info[3] = htonl(conn->rxgk.start_time >>  0);
+	info[4] = htonl(gk->key_number);
+
+	ret = crypto_krb5_calc_PRFplus(krb5, &rxgk->key, L, &conn_info, TK, gfp);
+	kfree_sensitive(buffer);
+	_leave(" = %d", ret);
+	return ret;
+}
+
+/*
+ * Derive a cipher.
+ */
+static int rxgk_derive_cipher(const struct krb5_enctype *krb5,
+			      u32 *params, size_t plen, struct crypto_aead **_ci)
+{
+	struct crypto_aead *ci;
+	int ret;
+
+	ci = crypto_alloc_aead(krb5->aead.base.cra_name, 0, 0);
+	if (IS_ERR(ci))
+		return (PTR_ERR(ci) == -ENOENT) ? -ENOPKG : PTR_ERR(ci);
+
+	ret = crypto_aead_setkey(ci, (void *)params, plen);
+	if (ret < 0) {
+		crypto_free_aead(ci);
+		return ret;
+	}
+
+	*_ci = ci;
+	return 0;
+}
+
+/*
+ * Set up the ciphers for the usage keys.
+ */
+static int rxgk_set_up_ciphers(struct rxrpc_connection *conn,
+			       struct rxgk_context *gk,
+			       const struct rxgk_key *rxgk,
+			       gfp_t gfp)
+{
+	const struct krb5_enctype *krb5 = gk->krb5;
+	struct krb5_buffer TK;
+	__be32 *params, tx_usage, rx_usage;
+	size_t plen = 2 * sizeof(__be32) + krb5->key_bytes;
+	int ret;
+
+	params = kzalloc(plen, gfp);
+	if (!params)
+		return -ENOMEM;
+
+	TK.len = krb5->key_bytes;
+	TK.data = (void *)&params[2];
+
+	ret = rxgk_derive_transport_key(conn, gk, rxgk, &TK, gfp);
+	if (ret < 0)
+		goto out;
+
+	params[0] = htonl(KRB5_ENCRYPT_MODE);
+	params[1] = htonl(RXGK_CLIENT_ENC_RESPONSE);
+	ret = rxgk_derive_cipher(krb5, params, plen, &gk->resp_crypto);
+	if (ret < 0)
+		goto out;
+
+	if (conn->security_level == RXRPC_SECURITY_AUTH) {
+		params[0] = htonl(KRB5_CHECKSUM_MODE);
+		tx_usage  = htonl(RXGK_CLIENT_MIC_PACKET);
+		rx_usage  = htonl(RXGK_SERVER_MIC_PACKET);
+	} else {
+		params[0] = htonl(KRB5_ENCRYPT_MODE);
+		tx_usage  = htonl(RXGK_CLIENT_ENC_PACKET);
+		rx_usage  = htonl(RXGK_SERVER_ENC_PACKET);
+	}
+
+	if (rxrpc_conn_is_service(conn))
+		swap(tx_usage, rx_usage);
+
+	params[1] = tx_usage;
+	ret = rxgk_derive_cipher(krb5, params, plen, &gk->tx_crypto);
+	if (ret < 0)
+		goto out;
+
+	params[1] = rx_usage;
+	ret = rxgk_derive_cipher(krb5, params, plen, &gk->rx_crypto);
+	if (ret < 0)
+		goto out;
+
+	ret = 0;
+out:
+	kfree_sensitive(params);
+	return ret;
+}
+
+/*
+ * Derive a transport key for a connection and then derive a bunch of usage
+ * keys from it and set up ciphers using them.
+ */
+struct rxgk_context *rxgk_generate_transport_key(struct rxrpc_connection *conn,
+						 const struct rxgk_key *key,
+						 unsigned int key_number,
+						 gfp_t gfp)
+{
+	struct rxgk_context *gk;
+	unsigned long lifetime;
+	int ret;
+
+	_enter("");
+
+	gk = kzalloc(sizeof(*gk), GFP_KERNEL);
+	if (!gk)
+		return ERR_PTR(-ENOMEM);
+	refcount_set(&gk->usage, 1);
+	gk->key		= key;
+	gk->key_number	= key_number;
+
+	gk->krb5 = crypto_krb5_find_enctype(key->enctype);
+	if (!gk->krb5) {
+		ret = -ENOPKG;
+		goto err_tk;
+	}
+
+	ret = rxgk_set_up_ciphers(conn, gk, key, gfp);
+	if (ret)
+		goto err_tk;
+
+	/* Set the remaining number of bytes encrypted with this key that may
+	 * be transmitted before rekeying.  Note that the spec has been
+	 * interpreted differently on this point...
+	 */
+	switch (key->bytelife) {
+	case 0:
+	case 63:
+		gk->bytes_remaining = LLONG_MAX;
+		break;
+	case 1 ... 62:
+		gk->bytes_remaining = 1LL << key->bytelife;
+		break;
+	default:
+		gk->bytes_remaining = key->bytelife;
+		break;
+	}
+
+	/* Set the time after which rekeying must occur */
+	if (key->lifetime) {
+		lifetime = min_t(u64, key->lifetime, INT_MAX / HZ);
+		lifetime *= HZ;
+	} else {
+		lifetime = MAX_JIFFY_OFFSET;
+	}
+	gk->expiry = jiffies + lifetime;
+	return gk;
+
+err_tk:
+	rxgk_put(gk);
+	_leave(" = %d", ret);
+	return ERR_PTR(ret);
+}
+
+/*
+ * Use the server secret key to set up the ciphers that will be used to extract
+ * the token from a response packet.
+ */
+struct crypto_aead *rxgk_set_up_token_cipher(const struct krb5_buffer *server_key,
+					     unsigned int enctype, gfp_t gfp)
+{
+	const struct krb5_enctype *krb5;
+	struct crypto_aead *ci;
+	__be32 *params;
+	size_t plen = 2 * sizeof(__be32) + server_key->len;
+	int ret;
+
+	krb5 = crypto_krb5_find_enctype(enctype);
+	if (!krb5)
+		return ERR_PTR(-ENOPKG);
+
+	params = kzalloc(plen, gfp);
+	if (!params)
+		return ERR_PTR(-ENOMEM);
+
+	params[0] = htonl(KRB5_ENCRYPT_MODE);
+	params[1] = htonl(RXGK_SERVER_ENC_TOKEN);
+	memcpy(&params[2], server_key->data, server_key->len);
+	ret = rxgk_derive_cipher(krb5, params, plen, &ci);
+	kfree_sensitive(params);
+	return ret < 0 ? ERR_PTR(ret) : ci;
+}



From dhowells at redhat.com  Thu Jan  9 17:03:09 2025
From: dhowells at redhat.com (David Howells)
Date: Fri, 10 Jan 2025 01:03:09 +0000
Subject: [RFC PATCH 7/8] rxrpc: rxgk: Implement the yfs-rxgk security class (GSSAPI)
In-Reply-To: <20250110010313.1471063-1-dhowells@redhat.com>
References: <20250110010313.1471063-1-dhowells@redhat.com>
Message-ID: <20250110010313.1471063-8-dhowells@redhat.com>

Implement the basic parts of the yfs-rxgk security class (security index 6)
to support GSSAPI-negotiated security.

Signed-off-by: David Howells <dhowells at redhat.com>
cc: Herbert Xu <herbert at gondor.apana.org.au>
cc: "David S. Miller" <davem at davemloft.net>
cc: Chuck Lever <chuck.lever at oracle.com>
cc: Marc Dionne <marc.dionne at auristor.com>
cc: Eric Dumazet <edumazet at google.com>
cc: Jakub Kicinski <kuba at kernel.org>
cc: Paolo Abeni <pabeni at redhat.com>
cc: Simon Horman <horms at kernel.org>
cc: linux-afs at lists.infradead.org
cc: linux-nfs at vger.kernel.org
cc: linux-crypto at vger.kernel.org
cc: netdev at vger.kernel.org
---
 include/trace/events/rxrpc.h |   36 ++
 net/rxrpc/Makefile           |    2 +
 net/rxrpc/ar-internal.h      |   16 +
 net/rxrpc/conn_event.c       |    2 +-
 net/rxrpc/output.c           |    2 +-
 net/rxrpc/protocol.h         |   20 +
 net/rxrpc/rxgk.c             | 1100 ++++++++++++++++++++++++++++++++++
 net/rxrpc/rxgk_app.c         |  318 ++++++++++
 net/rxrpc/rxgk_common.h      |   14 +
 net/rxrpc/rxkad.c            |    6 +-
 net/rxrpc/security.c         |    3 +
 11 files changed, 1515 insertions(+), 4 deletions(-)
 create mode 100644 net/rxrpc/rxgk.c
 create mode 100644 net/rxrpc/rxgk_app.c

diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h
index 2f119d18a061..168f3ab7c0e4 100644
--- a/include/trace/events/rxrpc.h
+++ b/include/trace/events/rxrpc.h
@@ -68,6 +68,38 @@
 	EM(rxkad_abort_resp_tkt_sname,		"rxkad-resp-tk-sname")	\
 	EM(rxkad_abort_resp_unknown_tkt,	"rxkad-resp-unknown-tkt") \
 	EM(rxkad_abort_resp_version,		"rxkad-resp-version")	\
+	/* RxGK security errors */					\
+	EM(rxgk_abort_1_verify_mic_eproto,	"rxgk1-vfy-mic-eproto")	\
+	EM(rxgk_abort_2_decrypt_eproto,		"rxgk2-dec-eproto")	\
+	EM(rxgk_abort_2_short_data,		"rxgk2-short-data")	\
+	EM(rxgk_abort_2_short_encdata,		"rxgk2-short-encdata")	\
+	EM(rxgk_abort_2_short_header,		"rxgk2-short-hdr")	\
+	EM(rxgk_abort_bad_key_number,		"rxgk-bad-key-num")	\
+	EM(rxgk_abort_chall_key_expired,	"rxgk-chall-key-exp")	\
+	EM(rxgk_abort_chall_no_key,		"rxgk-chall-nokey")	\
+	EM(rxgk_abort_chall_short,		"rxgk-chall-short")	\
+	EM(rxgk_abort_resp_auth_dec,		"rxgk-resp-auth-dec")	\
+	EM(rxgk_abort_resp_bad_callid,		"rxgk-resp-bad-callid")	\
+	EM(rxgk_abort_resp_bad_nonce,		"rxgk-resp-bad-nonce")	\
+	EM(rxgk_abort_resp_bad_param,		"rxgk-resp-bad-param")	\
+	EM(rxgk_abort_resp_call_ctr,		"rxgk-resp-call-ctr")	\
+	EM(rxgk_abort_resp_call_state,		"rxgk-resp-call-state")	\
+	EM(rxgk_abort_resp_internal_error,	"rxgk-resp-int-error")	\
+	EM(rxgk_abort_resp_nopkg,		"rxgk-resp-nopkg")	\
+	EM(rxgk_abort_resp_short_applen,	"rxgk-resp-short-applen") \
+	EM(rxgk_abort_resp_short_auth,		"rxgk-resp-short-auth") \
+	EM(rxgk_abort_resp_short_call_list,	"rxgk-resp-short-callls") \
+	EM(rxgk_abort_resp_short_packet,	"rxgk-resp-short-packet") \
+	EM(rxgk_abort_resp_short_yfs_klen,	"rxgk-resp-short-yfs-klen") \
+	EM(rxgk_abort_resp_short_yfs_key,	"rxgk-resp-short-yfs-key") \
+	EM(rxgk_abort_resp_short_yfs_tkt,	"rxgk-resp-short-yfs-tkt") \
+	EM(rxgk_abort_resp_tok_dec,		"rxgk-resp-tok-dec")	\
+	EM(rxgk_abort_resp_tok_internal_error,	"rxgk-resp-tok-int-err") \
+	EM(rxgk_abort_resp_tok_keyerr,		"rxgk-resp-tok-keyerr")	\
+	EM(rxgk_abort_resp_tok_nokey,		"rxgk-resp-tok-nokey")	\
+	EM(rxgk_abort_resp_tok_nopkg,		"rxgk-resp-tok-nopkg")	\
+	EM(rxgk_abort_resp_tok_short,		"rxgk-resp-tok-short")	\
+	EM(rxgk_abort_resp_xdr_align,		"rxgk-resp-xdr-align")	\
 	/* rxrpc errors */						\
 	EM(rxrpc_abort_call_improper_term,	"call-improper-term")	\
 	EM(rxrpc_abort_call_reset,		"call-reset")		\
@@ -454,6 +486,8 @@
 	EM(rxrpc_tx_point_call_final_resend,	"CallFinalResend") \
 	EM(rxrpc_tx_point_conn_abort,		"ConnAbort") \
 	EM(rxrpc_tx_point_reject,		"Reject") \
+	EM(rxrpc_tx_point_rxgk_challenge,	"RxGKChall") \
+	EM(rxrpc_tx_point_rxgk_response,	"RxGKResp") \
 	EM(rxrpc_tx_point_rxkad_challenge,	"RxkadChall") \
 	EM(rxrpc_tx_point_rxkad_response,	"RxkadResp") \
 	EM(rxrpc_tx_point_version_keepalive,	"VerKeepalive") \
@@ -472,6 +506,7 @@
 
 #define rxrpc_txbuf_traces \
 	EM(rxrpc_txbuf_alloc_data,		"ALLOC DATA ")	\
+	EM(rxrpc_txbuf_alloc_response,		"ALLOC RESP ")	\
 	EM(rxrpc_txbuf_free,			"FREE       ")	\
 	EM(rxrpc_txbuf_get_buffer,		"GET BUFFER ")	\
 	EM(rxrpc_txbuf_get_trans,		"GET TRANS  ")	\
@@ -479,6 +514,7 @@
 	EM(rxrpc_txbuf_put_cleaned,		"PUT CLEANED")	\
 	EM(rxrpc_txbuf_put_nomem,		"PUT NOMEM  ")	\
 	EM(rxrpc_txbuf_put_rotated,		"PUT ROTATED")	\
+	EM(rxrpc_txbuf_put_response_tx,		"PUT RESP TX")	\
 	EM(rxrpc_txbuf_put_send_aborted,	"PUT SEND-X ")	\
 	EM(rxrpc_txbuf_put_trans,		"PUT TRANS  ")	\
 	EM(rxrpc_txbuf_see_lost,		"SEE LOST   ")	\
diff --git a/net/rxrpc/Makefile b/net/rxrpc/Makefile
index 9c8eb1471054..2ef05701d6d1 100644
--- a/net/rxrpc/Makefile
+++ b/net/rxrpc/Makefile
@@ -40,6 +40,8 @@ rxrpc-$(CONFIG_PROC_FS) += proc.o
 rxrpc-$(CONFIG_RXKAD) += rxkad.o
 rxrpc-$(CONFIG_SYSCTL) += sysctl.o
 rxrpc-$(CONFIG_RXGK) += \
+	rxgk.o \
+	rxgk_app.o \
 	rxgk_kdf.o
 
 obj-$(CONFIG_RXPERF) += rxperf.o
diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h
index 2392f2e062c2..736dc6ea20ac 100644
--- a/net/rxrpc/ar-internal.h
+++ b/net/rxrpc/ar-internal.h
@@ -31,6 +31,7 @@ struct key_preparsed_payload;
 struct rxrpc_connection;
 struct rxrpc_txbuf;
 struct rxrpc_txqueue;
+struct rxgk_context;
 
 /*
  * Mark applied to socket buffers in skb->mark.  skb->priority is used
@@ -280,6 +281,11 @@ struct rxrpc_security {
 
 	/* clear connection security */
 	void (*clear)(struct rxrpc_connection *);
+
+	/* Default ticket -> key decoder */
+	int (*default_decode_ticket)(struct rxrpc_connection *conn, struct sk_buff *skb,
+				     unsigned int ticket_offset, unsigned int ticket_len,
+				     struct key **_key);
 };
 
 /*
@@ -527,7 +533,9 @@ struct rxrpc_connection {
 			u32	nonce;		/* response re-use preventer */
 		} rxkad;
 		struct {
+			struct rxgk_context *keys[1];
 			u64	start_time;	/* The start time for TK derivation */
+			u8	nonce[20];	/* Response re-use preventer */
 		} rxgk;
 	};
 	unsigned long		flags;
@@ -870,6 +878,8 @@ struct rxrpc_txbuf {
 	unsigned short		len;		/* Amount of data in buffer */
 	unsigned short		space;		/* Remaining data space */
 	unsigned short		offset;		/* Offset of fill point */
+	unsigned short		crypto_header;	/* Size of crypto header */
+	unsigned short		sec_header;	/* Size of security header */
 	unsigned short		pkt_len;	/* Size of packet content */
 	unsigned short		alloc_size;	/* Amount of bufferage allocated */
 	unsigned int		flags;
@@ -1295,6 +1305,7 @@ static inline struct rxrpc_net *rxrpc_net(struct net *net)
 /*
  * output.c
  */
+ssize_t do_udp_sendmsg(struct socket *socket, struct msghdr *msg, size_t len);
 void rxrpc_send_ACK(struct rxrpc_call *call, u8 ack_reason,
 		    rxrpc_serial_t serial, enum rxrpc_propose_ack_trace why);
 void rxrpc_send_probe_for_pmtud(struct rxrpc_call *call);
@@ -1366,6 +1377,11 @@ void rxrpc_call_add_rtt(struct rxrpc_call *call, enum rxrpc_rtt_rx_trace why,
 ktime_t rxrpc_get_rto_backoff(struct rxrpc_call *call, bool retrans);
 void rxrpc_call_init_rtt(struct rxrpc_call *call);
 
+/*
+ * rxgk.c
+ */
+extern const struct rxrpc_security rxgk_yfs;
+
 /*
  * rxkad.c
  */
diff --git a/net/rxrpc/conn_event.c b/net/rxrpc/conn_event.c
index 713e04394ceb..3c1663318626 100644
--- a/net/rxrpc/conn_event.c
+++ b/net/rxrpc/conn_event.c
@@ -57,7 +57,7 @@ int rxrpc_abort_conn(struct rxrpc_connection *conn, struct sk_buff *skb,
 				  sp->hdr.seq, abort_code, err);
 		rxrpc_poke_conn(conn, rxrpc_conn_get_poke_abort);
 	}
-	return -EPROTO;
+	return err;
 }
 
 /*
diff --git a/net/rxrpc/output.c b/net/rxrpc/output.c
index 6f7a125d6e90..6e2def0d8773 100644
--- a/net/rxrpc/output.c
+++ b/net/rxrpc/output.c
@@ -18,7 +18,7 @@
 
 extern int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len);
 
-static ssize_t do_udp_sendmsg(struct socket *socket, struct msghdr *msg, size_t len)
+ssize_t do_udp_sendmsg(struct socket *socket, struct msghdr *msg, size_t len)
 {
 	struct sockaddr *sa = msg->msg_name;
 	struct sock *sk = socket->sk;
diff --git a/net/rxrpc/protocol.h b/net/rxrpc/protocol.h
index 42f70e4636f8..f8bfec12bc7e 100644
--- a/net/rxrpc/protocol.h
+++ b/net/rxrpc/protocol.h
@@ -181,4 +181,24 @@ struct rxkad_response {
 	__be32		ticket_len;	/* Kerberos ticket length  */
 } __packed;
 
+/*
+ * GSSAPI security type-4 and type-6 data header.
+ */
+struct rxgk_header {
+	__be32	epoch;
+	__be32	cid;
+	__be32	call_number;
+	__be32	seq;
+	__be32	sec_index;
+	__be32	data_len;
+} __packed;
+
+/*
+ * GSSAPI security type-4 and type-6 response packet header.
+ */
+struct rxgk_response {
+	__be64	start_time;
+	__be32	token_len;
+} __packed;
+
 #endif /* _LINUX_RXRPC_PACKET_H */
diff --git a/net/rxrpc/rxgk.c b/net/rxrpc/rxgk.c
new file mode 100644
index 000000000000..7344f19b8ae2
--- /dev/null
+++ b/net/rxrpc/rxgk.c
@@ -0,0 +1,1100 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* GSSAPI-based RxRPC security
+ *
+ * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells at redhat.com)
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/net.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/key-type.h>
+#include "ar-internal.h"
+#include "rxgk_common.h"
+
+/*
+ * Parse the information from a server key
+ */
+static int rxgk_preparse_server_key(struct key_preparsed_payload *prep)
+{
+	const struct krb5_enctype *krb5;
+	struct krb5_buffer *server_key = (void *)&prep->payload.data[2];
+	unsigned int service, sec_class, kvno, enctype;
+	int n = 0;
+
+	_enter("%zu", prep->datalen);
+
+	if (sscanf(prep->orig_description, "%u:%u:%u:%u%n",
+		   &service, &sec_class, &kvno, &enctype, &n) != 4)
+		return -EINVAL;
+
+	if (prep->orig_description[n])
+		return -EINVAL;
+
+	krb5 = crypto_krb5_find_enctype(enctype);
+	if (!krb5)
+		return -ENOPKG;
+
+	prep->payload.data[0] = (struct krb5_enctype *)krb5;
+
+	if (prep->datalen != krb5->key_len)
+		return -EKEYREJECTED;
+
+	server_key->len = prep->datalen;
+	server_key->data = kmemdup(prep->data, prep->datalen, GFP_KERNEL);
+	if (!server_key->data)
+		return -ENOMEM;
+
+	_leave(" = 0");
+	return 0;
+}
+
+static void rxgk_free_server_key(union key_payload *payload)
+{
+	struct krb5_buffer *server_key = (void *)&payload->data[2];
+
+	kfree_sensitive(server_key->data);
+}
+
+static void rxgk_free_preparse_server_key(struct key_preparsed_payload *prep)
+{
+	rxgk_free_server_key(&prep->payload);
+}
+
+static void rxgk_destroy_server_key(struct key *key)
+{
+	rxgk_free_server_key(&key->payload);
+}
+
+static void rxgk_describe_server_key(const struct key *key, struct seq_file *m)
+{
+	const struct krb5_enctype *krb5 = key->payload.data[0];
+
+	if (krb5)
+		seq_printf(m, ": %s", krb5->name);
+}
+
+static struct rxgk_context *rxgk_get_key(struct rxrpc_connection *conn,
+					 u16 *specific_key_number)
+{
+	refcount_inc(&conn->rxgk.keys[0]->usage);
+	return conn->rxgk.keys[0];
+}
+
+/*
+ * initialise connection security
+ */
+static int rxgk_init_connection_security(struct rxrpc_connection *conn,
+					 struct rxrpc_key_token *token)
+{
+	struct rxgk_context *gk;
+	int ret;
+
+	_enter("{%d},{%x}", conn->debug_id, key_serial(conn->key));
+
+	conn->security_ix = token->security_index;
+	conn->security_level = token->rxgk->level;
+
+	if (rxrpc_conn_is_client(conn)) {
+		conn->rxgk.start_time = ktime_get();
+		do_div(conn->rxgk.start_time, 100);
+	}
+
+	gk = rxgk_generate_transport_key(conn, token->rxgk, 0, GFP_NOFS);
+	if (IS_ERR(gk))
+		return PTR_ERR(gk);
+	conn->rxgk.keys[0] = gk;
+
+	switch (conn->security_level) {
+	case RXRPC_SECURITY_PLAIN:
+	case RXRPC_SECURITY_AUTH:
+	case RXRPC_SECURITY_ENCRYPT:
+		break;
+	default:
+		ret = -EKEYREJECTED;
+		goto error;
+	}
+
+	ret = 0;
+error:
+	_leave(" = %d", ret);
+	return ret;
+}
+
+/*
+ * Clean up the crypto on a call.
+ */
+static void rxgk_free_call_crypto(struct rxrpc_call *call)
+{
+}
+
+/*
+ * Work out how much data we can put in a packet.
+ */
+static struct rxrpc_txbuf *rxgk_alloc_txbuf(struct rxrpc_call *call, size_t remain, gfp_t gfp)
+{
+	enum krb5_crypto_mode mode;
+	struct rxgk_context *gk;
+	struct rxrpc_txbuf *txb;
+	size_t shdr, alloc, limit, part, offset, gap;
+
+	switch (call->conn->security_level) {
+	default:
+		alloc = umin(remain, RXRPC_JUMBO_DATALEN);
+		return rxrpc_alloc_data_txbuf(call, alloc, 1, gfp);
+	case RXRPC_SECURITY_AUTH:
+		shdr = 0;
+		mode = KRB5_CHECKSUM_MODE;
+		break;
+	case RXRPC_SECURITY_ENCRYPT:
+		shdr = sizeof(struct rxgk_header);
+		mode = KRB5_ENCRYPT_MODE;
+		break;
+	}
+
+	gk = rxgk_get_key(call->conn, NULL);
+	if (IS_ERR(gk))
+		return NULL;
+
+	/* Work out the maximum amount of data that will fit. */
+	alloc = RXRPC_JUMBO_DATALEN;
+	limit = crypto_krb5_how_much_data(gk->krb5, mode, &alloc, &offset);
+
+	if (remain < limit - shdr) {
+		part = remain;
+		alloc = crypto_krb5_how_much_buffer(gk->krb5, mode,
+						    shdr + part, &offset);
+		gap = 0;
+	} else {
+		part = limit - shdr;
+		gap = RXRPC_JUMBO_DATALEN - alloc;
+		alloc = RXRPC_JUMBO_DATALEN;
+	}
+
+	rxgk_put(gk);
+
+	txb = rxrpc_alloc_data_txbuf(call, alloc, 16, gfp);
+	if (!txb)
+		return NULL;
+
+	txb->crypto_header	= offset;
+	txb->sec_header		= shdr;
+	txb->offset		+= offset + shdr;
+	txb->space		= part;
+
+	/* Clear excess space in the packet */
+	if (gap) {
+		struct rxrpc_wire_header *whdr = txb->kvec[0].iov_base;
+		void *p = whdr + 1;
+
+		memset(p + alloc - gap, 0, gap);
+	}
+	return txb;
+}
+
+/*
+ * Integrity mode (sign a packet - level 1 security)
+ */
+static int rxgk_secure_packet_integrity(const struct rxrpc_call *call,
+					struct rxgk_context *gk,
+					struct rxrpc_txbuf *txb)
+{
+	struct rxrpc_wire_header *whdr = txb->kvec[0].iov_base;
+	struct aead_request *req;
+	struct rxgk_header *hdr;
+	struct scatterlist sg[2];
+	size_t data_offset;
+	void *payload = whdr + 1;
+	int ret = -ENOMEM;
+
+	_enter("");
+
+	req = kzalloc(sizeof(*req) + crypto_aead_reqsize(gk->tx_crypto) +
+		      sizeof(*hdr), GFP_NOFS);
+	if (!req)
+		goto error_gk;
+
+	txb->pkt_len = crypto_krb5_how_much_buffer(gk->krb5, KRB5_CHECKSUM_MODE,
+						   txb->len, &data_offset);
+
+	hdr = (void *)req + sizeof(*req) + crypto_aead_reqsize(gk->tx_crypto);
+	hdr->epoch	= htonl(call->conn->proto.epoch);
+	hdr->cid	= htonl(call->cid);
+	hdr->call_number = htonl(call->call_id);
+	hdr->seq	= htonl(txb->seq);
+	hdr->sec_index	= htonl(call->security_ix);
+	hdr->data_len	= htonl(txb->len);
+
+	sg_init_table(sg, 2);
+	sg_set_buf(&sg[0], hdr, sizeof(*hdr));
+	sg_set_buf(&sg[1], payload, txb->pkt_len);
+
+	aead_request_set_tfm(req, gk->tx_crypto);
+	aead_request_set_crypt(req, sg, sg, data_offset + txb->len, NULL);
+	aead_request_set_ad(req, sizeof(*hdr));
+
+	ret = crypto_aead_encrypt(req);
+	if (ret < 0)
+		goto error;
+
+	if (txb->pkt_len == RXRPC_JUMBO_DATALEN)
+		txb->jumboable = true;
+	gk->bytes_remaining -= txb->pkt_len;
+
+error:
+	aead_request_free(req);
+error_gk:
+	rxgk_put(gk);
+	_leave(" = %d", ret);
+	return ret;
+}
+
+/*
+ * wholly encrypt a packet (level 2 security)
+ */
+static int rxgk_secure_packet_encrypted(const struct rxrpc_call *call,
+					struct rxgk_context *gk,
+					struct rxrpc_txbuf *txb)
+{
+	struct rxrpc_wire_header *whdr = txb->kvec[0].iov_base;
+	struct aead_request *req;
+	struct rxgk_header *hdr;
+	struct scatterlist sg[1];
+	size_t data_offset, data_len;
+	void *payload = whdr + 1;
+	int ret;
+
+	_enter("%x", txb->len);
+
+	req = kzalloc(sizeof(*req) + crypto_aead_reqsize(gk->tx_crypto),
+		      GFP_NOFS);
+	if (!req)
+		goto error_gk;
+
+	data_len = sizeof(*hdr) + txb->len;
+	txb->pkt_len = crypto_krb5_how_much_buffer(gk->krb5, KRB5_ENCRYPT_MODE,
+						   data_len, &data_offset);
+
+	/* Insert the header into the buffer. */
+	hdr = payload + txb->crypto_header;
+	hdr->epoch	 = htonl(call->conn->proto.epoch);
+	hdr->cid	 = htonl(call->cid);
+	hdr->call_number = htonl(call->call_id);
+	hdr->seq	 = htonl(txb->seq);
+	hdr->sec_index	 = htonl(call->security_ix);
+	hdr->data_len	 = htonl(txb->len);
+
+	sg_init_one(&sg[0], payload, txb->pkt_len);
+	ret = crypto_krb5_confound_buffer(gk->krb5, sg, 1, NULL, 0, 0);
+	if (ret < 0)
+		goto error;
+
+	aead_request_set_tfm(req, gk->tx_crypto);
+	aead_request_set_crypt(req, sg, sg, data_offset + data_len, NULL);
+
+	ret = crypto_aead_encrypt(req);
+	if (ret < 0)
+		goto error;
+
+	if (txb->pkt_len == RXRPC_JUMBO_DATALEN)
+		txb->jumboable = true;
+	gk->bytes_remaining -= txb->pkt_len;
+
+error:
+	aead_request_free(req);
+error_gk:
+	rxgk_put(gk);
+	_leave(" = %d", ret);
+	return ret;
+}
+
+/*
+ * checksum an RxRPC packet header
+ */
+static int rxgk_secure_packet(struct rxrpc_call *call, struct rxrpc_txbuf *txb)
+{
+	struct rxgk_context *gk;
+	int ret;
+
+	_enter("{%d{%x}},{#%u},%u,",
+	       call->debug_id, key_serial(call->conn->key), txb->seq, txb->len);
+
+	gk = rxgk_get_key(call->conn, NULL);
+	if (IS_ERR(gk))
+		return PTR_ERR(gk) == -ESTALE ? -EKEYREJECTED : PTR_ERR(gk);
+
+	ret = key_validate(call->conn->key);
+	if (ret < 0)
+		return ret;
+
+	txb->cksum = htons(gk->key_number);
+
+	switch (call->conn->security_level) {
+	case RXRPC_SECURITY_PLAIN:
+		rxgk_put(gk);
+		txb->pkt_len = txb->len;
+		return 0;
+	case RXRPC_SECURITY_AUTH:
+		return rxgk_secure_packet_integrity(call, gk, txb);
+	case RXRPC_SECURITY_ENCRYPT:
+		return rxgk_secure_packet_encrypted(call, gk, txb);
+	default:
+		rxgk_put(gk);
+		return -EPERM;
+	}
+}
+
+/*
+ * Integrity mode (check the signature on a packet - level 1 security)
+ */
+static int rxgk_verify_packet_integrity(struct rxrpc_call *call,
+					struct rxgk_context *gk,
+					struct sk_buff *skb)
+{
+	struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
+	struct aead_request *req;
+	struct rxgk_header *hdr;
+	struct scatterlist sg[17];
+	unsigned int offset = sp->offset, len = sp->len;
+	size_t data_offset, data_len = len;
+	int ret = -ENOMEM, nr_sg;
+
+	_enter("");
+
+	data_offset = sp->offset;
+	data_len = len;
+	crypto_krb5_where_is_the_data(gk->krb5, KRB5_CHECKSUM_MODE,
+				      &data_offset, &data_len);
+	sp->offset	= data_offset;
+	sp->len		= data_len;
+
+	req = kzalloc(sizeof(*req) + crypto_aead_reqsize(gk->rx_crypto) +
+		      sizeof(*hdr), GFP_NOFS);
+	if (!req)
+		goto error_gk;
+
+	aead_request_set_tfm(req, gk->rx_crypto);
+
+	hdr = (void *)req + sizeof(*req) + crypto_aead_reqsize(gk->rx_crypto);
+	hdr->epoch	= htonl(call->conn->proto.epoch);
+	hdr->cid	= htonl(call->cid);
+	hdr->call_number = htonl(call->call_id);
+	hdr->seq	= htonl(sp->hdr.seq);
+	hdr->sec_index	= htonl(call->security_ix);
+	hdr->data_len	= htonl(data_len);
+
+	sg_init_table(sg, ARRAY_SIZE(sg));
+	sg_set_buf(&sg[0], hdr, sizeof(*hdr));
+	nr_sg = skb_to_sgvec(skb, sg + 1, offset, len);
+	if (unlikely(nr_sg < 0)) {
+		ret = nr_sg;
+		goto error;
+	}
+	nr_sg++;
+
+	aead_request_set_crypt(req, sg, sg, len, NULL);
+	aead_request_set_ad(req, sizeof(*hdr));
+
+	ret = crypto_aead_decrypt(req);
+	if (ret < 0) {
+		if (ret == -EPROTO)
+			rxrpc_abort_eproto(call, skb, RXGK_INCONSISTENCY,
+					   rxgk_abort_1_verify_mic_eproto);
+		goto error;
+	}
+
+	ret = 0;
+error:
+	aead_request_free(req);
+error_gk:
+	rxgk_put(gk);
+	_leave(" = %d", ret);
+	return ret;
+}
+
+/*
+ * Decrypt an encrypted packet (level 2 security).
+ */
+static int rxgk_verify_packet_encrypted(struct rxrpc_call *call,
+					struct rxgk_context *gk,
+					struct sk_buff *skb)
+{
+	struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
+	struct aead_request *req;
+	struct rxgk_header *hdr;
+	struct scatterlist sg[16];
+	size_t offset = sp->offset, len = sp->len;
+	int ret;
+
+	_enter("");
+
+	req = kzalloc(sizeof(*req) + crypto_aead_reqsize(gk->rx_crypto) +
+		      sizeof(*hdr), GFP_NOFS);
+	if (!req)
+		goto error_gk;
+
+	hdr = (void *)req + sizeof(*req) + crypto_aead_reqsize(gk->rx_crypto);
+
+	sg_init_table(sg, ARRAY_SIZE(sg));
+	ret = skb_to_sgvec(skb, sg, offset, len);
+	if (unlikely(ret < 0))
+		goto error;
+
+	aead_request_set_tfm(req, gk->rx_crypto);
+	aead_request_set_crypt(req, sg, sg, len, NULL);
+
+	ret = crypto_aead_decrypt(req);
+	if (ret < 0) {
+		if (ret == -EPROTO)
+			rxrpc_abort_eproto(call, skb, RXGK_INCONSISTENCY,
+					   rxgk_abort_2_decrypt_eproto);
+		goto error;
+	}
+
+	crypto_krb5_where_is_the_data(gk->krb5, KRB5_ENCRYPT_MODE,
+				      &offset, &len);
+
+	if (len < sizeof(*hdr)) {
+		ret = rxrpc_abort_eproto(call, skb, RXGK_PACKETSHORT,
+					 rxgk_abort_2_short_header);
+		goto error;
+	}
+
+	/* Extract the header from the skb */
+	ret = skb_copy_bits(skb, offset, hdr, sizeof(*hdr));
+	if (ret < 0) {
+		ret = rxrpc_abort_eproto(call, skb, RXGK_PACKETSHORT,
+					 rxgk_abort_2_short_encdata);
+		goto error;
+	}
+	offset += sizeof(*hdr);
+	len -= sizeof(*hdr);
+
+	if (ntohl(hdr->epoch)		!= call->conn->proto.epoch ||
+	    ntohl(hdr->cid)		!= call->cid ||
+	    ntohl(hdr->call_number)	!= call->call_id ||
+	    ntohl(hdr->seq)		!= sp->hdr.seq ||
+	    ntohl(hdr->sec_index)	!= call->security_ix ||
+	    ntohl(hdr->data_len)	> len) {
+		ret = rxrpc_abort_eproto(call, skb, RXGK_SEALED_INCON,
+					 rxgk_abort_2_short_data);
+		goto error;
+	}
+
+	sp->offset = offset;
+	sp->len = ntohl(hdr->data_len);
+	ret = 0;
+error:
+	aead_request_free(req);
+error_gk:
+	rxgk_put(gk);
+	_leave(" = %d", ret);
+	return ret;
+}
+
+/*
+ * Verify the security on a received packet or subpacket (if part of a
+ * jumbo packet).
+ */
+static int rxgk_verify_packet(struct rxrpc_call *call, struct sk_buff *skb)
+{
+	struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
+	struct rxgk_context *gk;
+	u16 key_number = sp->hdr.cksum;
+
+	_enter("{%d{%x}},{#%u}",
+	       call->debug_id, key_serial(call->conn->key), sp->hdr.seq);
+
+	gk = rxgk_get_key(call->conn, &key_number);
+	if (IS_ERR(gk)) {
+		switch (PTR_ERR(gk)) {
+		case -ESTALE:
+			return rxrpc_abort_eproto(call, skb, RXGK_BADKEYNO,
+						  rxgk_abort_bad_key_number);
+		default:
+			return PTR_ERR(gk);
+		}
+	}
+
+	switch (call->conn->security_level) {
+	case RXRPC_SECURITY_PLAIN:
+		return 0;
+	case RXRPC_SECURITY_AUTH:
+		return rxgk_verify_packet_integrity(call, gk, skb);
+	case RXRPC_SECURITY_ENCRYPT:
+		return rxgk_verify_packet_encrypted(call, gk, skb);
+	default:
+		rxgk_put(gk);
+		return -ENOANO;
+	}
+}
+
+/*
+ * Allocate memory to hold a challenge or a response packet.  We're not running
+ * in the io_thread, so we can't use ->tx_alloc.
+ */
+static struct page *rxgk_alloc_packet(size_t total_len)
+{
+	gfp_t gfp = GFP_NOFS;
+	int order;
+
+	order = get_order(total_len);
+	if (order > 0)
+		gfp |= __GFP_COMP;
+	return alloc_pages(gfp, order);
+}
+
+/*
+ * Issue a challenge.
+ */
+static int rxgk_issue_challenge(struct rxrpc_connection *conn)
+{
+	struct rxrpc_wire_header *whdr;
+	struct bio_vec bvec[1];
+	struct msghdr msg;
+	struct page *page;
+	size_t len = sizeof(*whdr) + sizeof(conn->rxgk.nonce);
+	u32 serial;
+	int ret;
+
+	_enter("{%d}", conn->debug_id);
+
+	get_random_bytes(&conn->rxgk.nonce, sizeof(conn->rxgk.nonce));
+
+	/* We can't use conn->tx_alloc without a lock */
+	page = rxgk_alloc_packet(sizeof(*whdr) + sizeof(conn->rxgk.nonce));
+	if (!page)
+		return -ENOMEM;
+
+	bvec[0].bv_page	= page;
+	bvec[0].bv_len	= len;
+	iov_iter_bvec(&msg.msg_iter, WRITE, bvec, 1, len);
+
+	msg.msg_name	= &conn->peer->srx.transport;
+	msg.msg_namelen	= conn->peer->srx.transport_len;
+	msg.msg_control	= NULL;
+	msg.msg_controllen = 0;
+	msg.msg_flags	= MSG_SPLICE_PAGES;
+
+	whdr = page_address(page);
+	whdr->epoch	= htonl(conn->proto.epoch);
+	whdr->cid	= htonl(conn->proto.cid);
+	whdr->callNumber = 0;
+	whdr->seq	= 0;
+	whdr->type	= RXRPC_PACKET_TYPE_CHALLENGE;
+	whdr->flags	= conn->out_clientflag;
+	whdr->userStatus = 0;
+	whdr->securityIndex = conn->security_ix;
+	whdr->_rsvd	= 0;
+	whdr->serviceId	= htons(conn->service_id);
+
+	memcpy(whdr + 1, conn->rxgk.nonce, sizeof(conn->rxgk.nonce));
+
+	serial = rxrpc_get_next_serials(conn, 1);
+	whdr->serial = htonl(serial);
+
+	ret = do_udp_sendmsg(conn->local->socket, &msg, len);
+	if (ret > 0)
+		conn->peer->last_tx_at = ktime_get_seconds();
+	__free_page(page);
+
+	if (ret < 0) {
+		trace_rxrpc_tx_fail(conn->debug_id, serial, ret,
+				    rxrpc_tx_point_rxgk_challenge);
+		return -EAGAIN;
+	}
+
+	trace_rxrpc_tx_packet(conn->debug_id, whdr,
+			      rxrpc_tx_point_rxgk_challenge);
+	_leave(" = 0");
+	return 0;
+}
+
+/*
+ * Send a response packet.
+ */
+static int rxgk_send_response(struct rxrpc_connection *conn,
+			      struct page *page, size_t total_len)
+{
+	struct rxrpc_wire_header *whdr = page_address(page);
+	struct bio_vec bvec[1];
+	struct msghdr msg;
+	u32 serial;
+	int ret;
+
+	_enter("");
+
+	msg.msg_name	= &conn->peer->srx.transport;
+	msg.msg_namelen	= conn->peer->srx.transport_len;
+	msg.msg_control	= NULL;
+	msg.msg_controllen = 0;
+	msg.msg_flags	= MSG_SPLICE_PAGES;
+
+	bvec[0].bv_page	= page;
+	bvec[0].bv_len	= total_len;
+	iov_iter_bvec(&msg.msg_iter, WRITE, bvec, 1, total_len);
+
+	serial = rxrpc_get_next_serials(conn, 1);
+	whdr->serial = htonl(serial);
+
+	ret = do_udp_sendmsg(conn->local->socket, &msg, total_len);
+	if (ret < 0) {
+		trace_rxrpc_tx_fail(conn->debug_id, serial, ret,
+				    rxrpc_tx_point_rxgk_response);
+		return -EAGAIN;
+	}
+
+	conn->peer->last_tx_at = ktime_get_seconds();
+	_leave(" = 0");
+	return 0;
+}
+
+/*
+ * Construct the authenticator to go in the response packet
+ *
+ * struct RXGK_Authenticator {
+ *	opaque nonce[20];
+ *	opaque appdata<>;
+ *	RXGK_Level level;
+ *	unsigned int epoch;
+ *	unsigned int cid;
+ *	unsigned int call_numbers<>;
+ * };
+ */
+static void rxgk_construct_authenticator(struct rxrpc_connection *conn,
+					 const u8 *nonce, __be32 *xdr)
+{
+	memcpy(xdr, nonce, 20);
+	xdr += 5;
+	*xdr++ = htonl(0); /* appdata len */
+	*xdr++ = htonl(conn->security_level);
+	*xdr++ = htonl(conn->proto.epoch);
+	*xdr++ = htonl(conn->proto.cid);
+	*xdr++ = htonl(4); /* # call_numbers */
+	*xdr++ = htonl(conn->channels[0].call_counter);
+	*xdr++ = htonl(conn->channels[1].call_counter);
+	*xdr++ = htonl(conn->channels[2].call_counter);
+	*xdr   = htonl(conn->channels[3].call_counter);
+}
+
+/*
+ * Construct the response.
+ *
+ * struct RXGK_Response {
+ *	rxgkTime start_time;
+ *	RXGK_Data token;
+ *	opaque authenticator<RXGK_MAXAUTHENTICATOR>
+ * };
+ */
+static int rxgk_construct_response(struct rxrpc_connection *conn,
+				   struct sk_buff *challenge,
+				   const u8 *nonce)
+{
+	struct rxrpc_wire_header *whdr;
+	struct rxrpc_skb_priv *csp = rxrpc_skb(challenge);
+	struct aead_request *req;
+	struct rxgk_context *gk;
+	struct scatterlist sg[1];
+	struct page *page;
+	size_t resp_len, auth_len, authx_len, auth_offset, authx_offset;
+	__be32 *xdr;
+	void *payload;
+	int ret;
+
+	gk = rxgk_get_key(conn, NULL);
+	if (IS_ERR(gk))
+		return PTR_ERR(gk);
+
+	ret = -ENOMEM;
+	req = kzalloc(sizeof(*req) + crypto_aead_reqsize(gk->resp_crypto), GFP_NOFS);
+	if (!req)
+		goto error_gk;
+
+	auth_len = 20 + 4 /* appdatalen */ + 12 + (1 + 4) * 4;
+	authx_len = crypto_krb5_how_much_buffer(gk->krb5, KRB5_ENCRYPT_MODE,
+						auth_len, &auth_offset);
+
+	resp_len  = 8;
+	resp_len += 4 + xdr_round_up(gk->key->ticket.len);
+	resp_len += 4 + xdr_round_up(authx_len);
+
+	page = rxgk_alloc_packet(sizeof(*whdr) + resp_len);
+	if (!page)
+		goto error_req;
+
+	whdr = page_address(page);
+	whdr->epoch		= htonl(csp->hdr.epoch);
+	whdr->cid		= htonl(csp->hdr.cid);
+	whdr->callNumber	= 0;
+	whdr->serial		= 0;
+	whdr->seq		= 0;
+	whdr->type		= RXRPC_PACKET_TYPE_RESPONSE;
+	whdr->flags		= conn->out_clientflag;
+	whdr->userStatus	= 0;
+	whdr->securityIndex	= csp->hdr.securityIndex;
+	whdr->cksum		= htons(gk->key_number);
+	whdr->serviceId		= htons(csp->hdr.serviceId);
+
+	payload = whdr + 1;
+	xdr = payload;
+
+	*xdr++ = htonl(upper_32_bits(conn->rxgk.start_time));
+	*xdr++ = htonl(lower_32_bits(conn->rxgk.start_time));
+	*xdr++ = htonl(gk->key->ticket.len);
+	memcpy(xdr, gk->key->ticket.data, xdr_round_up(gk->key->ticket.len));
+	xdr += xdr_round_up(gk->key->ticket.len) / sizeof(*xdr);
+	*xdr++ = htonl(authx_len);
+
+	authx_offset = (void *)xdr - payload;
+
+	xdr = (void *)payload + authx_offset + auth_offset;
+	rxgk_construct_authenticator(conn, nonce, xdr);
+
+	sg_init_one(sg, payload + authx_offset, authx_len);
+
+	aead_request_set_tfm(req, gk->resp_crypto);
+	aead_request_set_crypt(req, sg, sg, auth_offset + auth_len, NULL);
+
+	ret = crypto_aead_encrypt(req);
+	if (ret < 0)
+		goto error;
+
+	ret = rxgk_send_response(conn, page, sizeof(*whdr) + authx_offset + authx_len);
+error:
+	__free_page(page);
+error_req:
+	kfree_sensitive(req);
+error_gk:
+	rxgk_put(gk);
+	_leave(" = %d", ret);
+	return ret;
+}
+
+/*
+ * Respond to a challenge packet
+ */
+static int rxgk_respond_to_challenge(struct rxrpc_connection *conn,
+				     struct sk_buff *skb)
+{
+	u8 nonce[20];
+
+	_enter("{%d,%x}", conn->debug_id, key_serial(conn->key));
+
+	if (!conn->key)
+		return rxrpc_abort_conn(conn, skb, RX_PROTOCOL_ERROR, -EPROTO,
+					rxgk_abort_chall_no_key);
+
+	if (key_validate(conn->key) < 0)
+		return rxrpc_abort_conn(conn, skb, RXGK_EXPIRED, -EPROTO,
+					rxgk_abort_chall_key_expired);
+
+	if (skb_copy_bits(skb, sizeof(struct rxrpc_wire_header),
+			  nonce, sizeof(nonce)) < 0)
+		return rxrpc_abort_conn(conn, skb, RXGK_PACKETSHORT, -EPROTO,
+					rxgk_abort_chall_short);
+
+	return rxgk_construct_response(conn, skb, nonce);
+}
+
+/*
+ * Verify the authenticator.
+ *
+ * struct RXGK_Authenticator {
+ *	opaque nonce[20];
+ *	opaque appdata<>;
+ *	RXGK_Level level;
+ *	unsigned int epoch;
+ *	unsigned int cid;
+ *	unsigned int call_numbers<>;
+ * };
+ */
+static int rxgk_do_verify_authenticator(struct rxrpc_connection *conn,
+					const struct krb5_enctype *krb5,
+					struct sk_buff *skb,
+					__be32 *p, __be32 *end)
+{
+	u32 app_len, call_count, level, epoch, cid, i;
+
+	_enter("");
+
+	if (memcmp(p, conn->rxgk.nonce, 20) != 0)
+		return rxrpc_abort_conn(conn, skb, RXGK_NOTAUTH, -EPROTO,
+					rxgk_abort_resp_bad_nonce);
+	p += 20 / sizeof(__be32);
+
+	app_len	= ntohl(*p++);
+	if (app_len > (end - p) * sizeof(__be32))
+		return rxrpc_abort_conn(conn, skb, RXGK_NOTAUTH, -EPROTO,
+					rxgk_abort_resp_short_applen);
+
+	p += xdr_round_up(app_len) / sizeof(__be32);
+	if (end - p < 4)
+		return rxrpc_abort_conn(conn, skb, RXGK_NOTAUTH, -EPROTO,
+					rxgk_abort_resp_short_applen);
+
+	level	= ntohl(*p++);
+	epoch	= ntohl(*p++);
+	cid	= ntohl(*p++);
+	call_count = ntohl(*p++);
+
+	if (level	!= conn->security_level ||
+	    epoch	!= conn->proto.epoch ||
+	    cid		!= conn->proto.cid ||
+	    call_count	> 4)
+		return rxrpc_abort_conn(conn, skb, RXGK_NOTAUTH, -EPROTO,
+					rxgk_abort_resp_bad_param);
+
+	if (end - p < call_count)
+		return rxrpc_abort_conn(conn, skb, RXGK_NOTAUTH, -EPROTO,
+					rxgk_abort_resp_short_call_list);
+
+	for (i = 0; i < call_count; i++) {
+		u32 call_id = ntohl(*p++);
+
+		if (call_id > INT_MAX)
+			return rxrpc_abort_conn(conn, skb, RXGK_NOTAUTH, -EPROTO,
+						rxgk_abort_resp_bad_callid);
+
+		if (call_id < conn->channels[i].call_counter)
+			return rxrpc_abort_conn(conn, skb, RXGK_NOTAUTH, -EPROTO,
+						rxgk_abort_resp_call_ctr);
+
+		if (call_id > conn->channels[i].call_counter) {
+			if (conn->channels[i].call)
+				return rxrpc_abort_conn(conn, skb, RXGK_NOTAUTH, -EPROTO,
+							rxgk_abort_resp_call_state);
+
+			conn->channels[i].call_counter = call_id;
+		}
+	}
+
+	_leave(" = 0");
+	return 0;
+}
+
+/*
+ * Extract the authenticator and verify it.
+ */
+static int rxgk_verify_authenticator(struct rxrpc_connection *conn,
+				     const struct krb5_enctype *krb5,
+				     struct sk_buff *skb,
+				     unsigned int auth_offset, unsigned int auth_len)
+{
+	void *auth;
+	__be32 *p;
+	int ret;
+
+	auth = kmalloc(auth_len, GFP_NOFS);
+	if (!auth)
+		return -ENOMEM;
+
+	ret = skb_copy_bits(skb, auth_offset, auth, auth_len);
+	if (ret < 0) {
+		ret = rxrpc_abort_conn(conn, skb, RXGK_NOTAUTH, -EPROTO,
+				       rxgk_abort_resp_short_auth);
+		goto error;
+	}
+
+	p = auth;
+	ret = rxgk_do_verify_authenticator(conn, krb5, skb, p, p + auth_len);
+error:
+	kfree(auth);
+	return ret;
+}
+
+/*
+ * Verify a response.
+ *
+ * struct RXGK_Response {
+ *	rxgkTime	start_time;
+ *	RXGK_Data	token;
+ *	opaque		authenticator<RXGK_MAXAUTHENTICATOR>
+ * };
+ */
+static int rxgk_verify_response(struct rxrpc_connection *conn,
+				struct sk_buff *skb)
+{
+	const struct krb5_enctype *krb5;
+	struct rxrpc_key_token *token;
+	struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
+	struct rxgk_response rhdr;
+	struct rxgk_context *gk;
+	struct key *key = NULL;
+	unsigned int offset = sizeof(struct rxrpc_wire_header);
+	unsigned int len = skb->len - sizeof(struct rxrpc_wire_header);
+	unsigned int token_offset, token_len;
+	size_t auth_offset, auth_len;
+	__be32 xauth_len;
+	int ret;
+
+	_enter("{%d}", conn->debug_id);
+
+	/* Parse the RXGK_Response object */
+	if (sizeof(rhdr) + sizeof(__be32) > len)
+		goto short_packet;
+
+	if (skb_copy_bits(skb, offset, &rhdr, sizeof(rhdr)) < 0)
+		goto short_packet;
+	offset	+= sizeof(rhdr);
+	len	-= sizeof(rhdr);
+
+	token_offset	= offset;
+	token_len	= ntohl(rhdr.token_len);
+	if (xdr_round_up(token_len) + sizeof(__be32) > len)
+		goto short_packet;
+
+	offset	+= xdr_round_up(token_len);
+	len	-= xdr_round_up(token_len);
+
+	if (skb_copy_bits(skb, offset, &xauth_len, sizeof(xauth_len)) < 0)
+		goto short_packet;
+	offset	+= sizeof(xauth_len);
+	len	-= sizeof(xauth_len);
+
+	auth_offset	= offset;
+	auth_len	= ntohl(xauth_len);
+	if (auth_len < len)
+		goto short_packet;
+	if (auth_len & 3)
+		goto inconsistent;
+	if (auth_len < 20 + 9 * 4)
+		goto auth_too_short;
+
+	/* We need to extract and decrypt the token and instantiate a session
+	 * key for it.  This bit, however, is application-specific.  If
+	 * possible, we use a default parser, but we might end up bumping this
+	 * to the app to deal with - which might mean a round trip to
+	 * userspace.
+	 */
+	ret = rxgk_extract_token(conn, skb, token_offset, token_len, &key);
+	if (ret < 0)
+		goto out;
+
+	/* We now have a key instantiated from the decrypted ticket.  We can
+	 * pass this to the application so that they can parse the ticket
+	 * content and we can use the session key it contains to derive the
+	 * keys we need.
+	 *
+	 * Note that we have to switch enctype at this point as the enctype of
+	 * the ticket doesn't necessarily match that of the transport.
+	 */
+	token = key->payload.data[0];
+	conn->security_level = token->rxgk->level;
+	conn->rxgk.start_time = __be64_to_cpu(rhdr.start_time);
+
+	gk = rxgk_generate_transport_key(conn, token->rxgk, sp->hdr.cksum, GFP_NOFS);
+	if (IS_ERR(gk)) {
+		ret = PTR_ERR(gk);
+		goto cant_get_token;
+	}
+
+	krb5 = gk->krb5;
+
+	/* Decrypt, parse and verify the authenticator. */
+	ret = rxgk_decrypt_skb(gk->resp_crypto, skb,
+			       &auth_offset, &auth_len);
+	if (ret < 0) {
+		rxrpc_abort_conn(conn, skb, RXGK_SEALED_INCON, ret,
+				 rxgk_abort_resp_auth_dec);
+		goto out;
+	}
+
+	ret = rxgk_verify_authenticator(conn, krb5, skb, auth_offset, auth_len);
+	if (ret < 0)
+		goto out;
+
+	conn->key = key;
+	key = NULL;
+	ret = 0;
+out:
+	key_put(key);
+	_leave(" = %d", ret);
+	return ret;
+
+inconsistent:
+	ret = rxrpc_abort_conn(conn, skb, RXGK_INCONSISTENCY, -EPROTO,
+			       rxgk_abort_resp_xdr_align);
+	goto out;
+auth_too_short:
+	ret = rxrpc_abort_conn(conn, skb, RXGK_PACKETSHORT, -EPROTO,
+			       rxgk_abort_resp_short_auth);
+	goto out;
+short_packet:
+	ret = rxrpc_abort_conn(conn, skb, RXGK_PACKETSHORT, -EPROTO,
+			       rxgk_abort_resp_short_packet);
+	goto out;
+
+cant_get_token:
+	switch (ret) {
+	case -ENOMEM:
+		goto temporary_error;
+	case -EINVAL:
+		ret = rxrpc_abort_conn(conn, skb, RXGK_NOTAUTH, -EKEYREJECTED,
+				       rxgk_abort_resp_internal_error);
+		goto out;
+	case -ENOPKG:
+		ret = rxrpc_abort_conn(conn, skb, RXGK_BADETYPE, -EKEYREJECTED,
+				       rxgk_abort_resp_nopkg);
+		goto out;
+	}
+
+temporary_error:
+	/* Ignore the response packet if we got a temporary error such as
+	 * ENOMEM.  We just want to send the challenge again.  Note that we
+	 * also come out this way if the ticket decryption fails.
+	 */
+	goto out;
+}
+
+/*
+ * clear the connection security
+ */
+static void rxgk_clear(struct rxrpc_connection *conn)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(conn->rxgk.keys); i++)
+		rxgk_put(conn->rxgk.keys[i]);
+}
+
+/*
+ * Initialise the RxGK security service.
+ */
+static int rxgk_init(void)
+{
+	return 0;
+}
+
+/*
+ * Clean up the RxGK security service.
+ */
+static void rxgk_exit(void)
+{
+}
+
+/*
+ * RxRPC YFS GSSAPI-based security
+ */
+const struct rxrpc_security rxgk_yfs = {
+	.name				= "yfs-rxgk",
+	.security_index			= RXRPC_SECURITY_YFS_RXGK,
+	.no_key_abort			= RXGK_NOTAUTH,
+	.init				= rxgk_init,
+	.exit				= rxgk_exit,
+	.preparse_server_key		= rxgk_preparse_server_key,
+	.free_preparse_server_key	= rxgk_free_preparse_server_key,
+	.destroy_server_key		= rxgk_destroy_server_key,
+	.describe_server_key		= rxgk_describe_server_key,
+	.init_connection_security	= rxgk_init_connection_security,
+	.alloc_txbuf			= rxgk_alloc_txbuf,
+	.secure_packet			= rxgk_secure_packet,
+	.verify_packet			= rxgk_verify_packet,
+	.free_call_crypto		= rxgk_free_call_crypto,
+	.issue_challenge		= rxgk_issue_challenge,
+	.respond_to_challenge		= rxgk_respond_to_challenge,
+	.verify_response		= rxgk_verify_response,
+	.clear				= rxgk_clear,
+	.default_decode_ticket		= rxgk_yfs_decode_ticket,
+};
diff --git a/net/rxrpc/rxgk_app.c b/net/rxrpc/rxgk_app.c
new file mode 100644
index 000000000000..1b4e322d619e
--- /dev/null
+++ b/net/rxrpc/rxgk_app.c
@@ -0,0 +1,318 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Application-specific bits for GSSAPI-based RxRPC security
+ *
+ * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells at redhat.com)
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/net.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/key-type.h>
+#include "ar-internal.h"
+#include "rxgk_common.h"
+
+/*
+ * Decode a default-style YFS ticket in a response and turn it into an
+ * rxrpc-type key.
+ *
+ * struct rxgk_key {
+ *	afs_uint32	enctype;
+ *	opaque		key<>;
+ * };
+ *
+ * struct RXGK_AuthName {
+ *	afs_int32	kind;
+ *	opaque		data<AUTHDATAMAX>;
+ *	opaque		display<AUTHPRINTABLEMAX>;
+ * };
+ *
+ * struct RXGK_Token {
+ *	rxgk_key		K0;
+ *	RXGK_Level		level;
+ *	rxgkTime		starttime;
+ *	afs_int32		lifetime;
+ *	afs_int32		bytelife;
+ *	rxgkTime		expirationtime;
+ *	struct RXGK_AuthName	identities<>;
+ * };
+ */
+int rxgk_yfs_decode_ticket(struct rxrpc_connection *conn, struct sk_buff *skb,
+			   unsigned int ticket_offset, unsigned int ticket_len,
+			   struct key **_key)
+{
+	struct rxrpc_key_token *token;
+	const struct cred *cred = current_cred(); // TODO - use socket creds
+	struct key *key;
+	size_t pre_ticket_len, payload_len;
+	unsigned int klen, enctype;
+	void *payload, *ticket;
+	__be32 *t, *p, *q, tmp[2];
+	int ret;
+
+	_enter("");
+
+	/* Get the session key length */
+	ret = skb_copy_bits(skb, ticket_offset, tmp, sizeof(tmp));
+	if (ret < 0)
+		return rxrpc_abort_conn(conn, skb, RXGK_INCONSISTENCY, -EPROTO,
+					rxgk_abort_resp_short_yfs_klen);
+	enctype = ntohl(tmp[0]);
+	klen = ntohl(tmp[1]);
+
+	if (klen > ticket_len - 10 * sizeof(__be32))
+		return rxrpc_abort_conn(conn, skb, RXGK_INCONSISTENCY, -EPROTO,
+					rxgk_abort_resp_short_yfs_key);
+
+	pre_ticket_len = ((5 + 14) * sizeof(__be32) +
+			  xdr_round_up(klen) +
+			  sizeof(__be32));
+	payload_len = pre_ticket_len + xdr_round_up(ticket_len);
+
+	payload = kzalloc(payload_len, GFP_NOFS);
+	if (!payload)
+		return -ENOMEM;
+
+	/* We need to fill out the XDR form for a key payload that we can pass
+	 * to add_key().  Start by copying in the ticket so that we can parse
+	 * it.
+	 */
+	ticket = payload + pre_ticket_len;
+	ret = skb_copy_bits(skb, ticket_offset, ticket, ticket_len);
+	if (ret < 0) {
+		ret = rxrpc_abort_conn(conn, skb, RXGK_INCONSISTENCY, -EPROTO,
+				       rxgk_abort_resp_short_yfs_tkt);
+		goto error;
+	}
+
+	/* Fill out the form header. */
+	p = payload;
+	p[0] = htonl(0); /* Flags */
+	p[1] = htonl(1); /* len(cellname) */
+	p[2] = htonl(0x20000000); /* Cellname " " */
+	p[3] = htonl(1); /* #tokens */
+	p[4] = htonl(15 * sizeof(__be32) + xdr_round_up(klen) +
+		     xdr_round_up(ticket_len)); /* Token len */
+
+	/* Now fill in the body.  Most of this we can just scrape directly from
+	 * the ticket.
+	 */
+	t = ticket + sizeof(__be32) * 2 + xdr_round_up(klen);
+	q = payload + 5 * sizeof(__be32);
+	q[ 0] = htonl(RXRPC_SECURITY_YFS_RXGK);
+	q[ 1] = t[1];		/* begintime - msw */
+	q[ 2] = t[2];		/* - lsw */
+	q[ 3] = t[5];		/* endtime - msw */
+	q[ 4] = t[6];		/* - lsw */
+	q[ 5] = 0;		/* level - msw */
+	q[ 6] = t[0];		/* - lsw */
+	q[ 7] = 0;		/* lifetime - msw */
+	q[ 8] = t[3];		/* - lsw */
+	q[ 9] = 0;		/* bytelife - msw */
+	q[10] = t[4];		/* - lsw */
+	q[11] = 0;		/* enctype - msw */
+	q[12] = htonl(enctype);	/* - lsw */
+	q[13] = htonl(klen);	/* Key length */
+
+	q += 14;
+
+	memcpy(q, ticket + sizeof(__be32) * 2, klen);
+	q += xdr_round_up(klen) / 4;
+	q[0] = htonl(ticket_len);
+	q++;
+	if (WARN_ON((unsigned long)q != (unsigned long)ticket)) {
+		ret = -EIO;
+		goto error;
+	}
+
+	/* Ticket read in with skb_copy_bits above */
+	q += xdr_round_up(ticket_len) / 4;
+	if (WARN_ON((unsigned long)q - (unsigned long)payload != payload_len)) {
+		ret = -EIO;
+		goto error;
+	}
+
+	/* Now turn that into a key. */
+	key = key_alloc(&key_type_rxrpc, "x",
+			GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, 0, // TODO: Use socket owner
+			KEY_ALLOC_NOT_IN_QUOTA, NULL);
+	if (IS_ERR(key)) {
+		_leave(" = -ENOMEM [alloc %ld]", PTR_ERR(key));
+		goto error;
+	}
+
+	_debug("key %d", key_serial(key));
+
+	ret = key_instantiate_and_link(key, payload, payload_len, NULL, NULL);
+	if (ret < 0)
+		goto error_key;
+
+	token = key->payload.data[0];
+	token->no_leak_key = true;
+	*_key = key;
+	key = NULL;
+	ret = 0;
+	goto error;
+
+error_key:
+	key_put(key);
+error:
+	kfree_sensitive(payload);
+	_leave(" = %d", ret);
+	return ret;
+}
+
+/*
+ * Decrypt the token.
+ */
+int rxgk_decrypt_skb(struct crypto_aead *token_enc, struct sk_buff *skb,
+		     size_t *_offset, size_t *_len)
+{
+	struct krb5_enctype *krb5 = crypto_krb5_enctype(token_enc);
+	struct aead_request *req;
+	struct scatterlist sg[16];
+	size_t len = *_len;
+	int ret;
+
+	req = kzalloc(sizeof(*req) + crypto_aead_reqsize(token_enc), GFP_NOFS);
+	if (!req)
+		return -ENOMEM;
+
+	sg_init_table(sg, ARRAY_SIZE(sg));
+	ret = skb_to_sgvec(skb, sg, *_offset, len);
+	if (unlikely(ret < 0))
+		goto out;
+
+	aead_request_set_tfm(req, token_enc);
+	aead_request_set_crypt(req, sg, sg, len, NULL);
+
+	ret = crypto_aead_decrypt(req);
+	if (ret < 0)
+		goto out;
+
+	crypto_krb5_where_is_the_data(krb5, KRB5_ENCRYPT_MODE, _offset, _len);
+	ret = 0;
+out:
+	kfree_sensitive(req);
+	return ret;
+}
+
+/*
+ * Extract the token and set up a session key from the details.
+ *
+ * struct RXGK_TokenContainer {
+ *	afs_int32	kvno;
+ *	afs_int32	enctype;
+ *	opaque		encrypted_token<>;
+ * };
+ *
+ * [tools.ietf.org/html/draft-wilkinson-afs3-rxgk-afs-08 sec 6.1]
+ */
+int rxgk_extract_token(struct rxrpc_connection *conn, struct sk_buff *skb,
+		       unsigned int token_offset, unsigned int token_len,
+		       struct key **_key)
+{
+	const struct krb5_buffer *server_secret;
+	struct crypto_aead *token_enc = NULL;
+	struct key *server_key;
+	size_t ticket_offset, ticket_len;
+	u32 kvno, enctype;
+	int ret;
+
+	struct {
+		__be32 kvno;
+		__be32 enctype;
+		__be32 token_len;
+	} container;
+
+	/* Decode the RXGK_TokenContainer object.  This tells us which server
+	 * key we should be using.  We can then fetch the key, get the secret
+	 * and set up the crypto to extract the token.
+	 */
+	if (skb_copy_bits(skb, token_offset, &container, sizeof(container)) < 0)
+		return rxrpc_abort_conn(conn, skb, RXGK_PACKETSHORT, -EPROTO,
+					rxgk_abort_resp_tok_short);
+
+	kvno		= ntohl(container.kvno);
+	enctype		= ntohl(container.enctype);
+	ticket_len	= ntohl(container.token_len);
+	ticket_offset	= token_offset + sizeof(container);
+
+	if (xdr_round_up(ticket_len) > token_len - 3 * 4)
+		return rxrpc_abort_conn(conn, skb, RXGK_PACKETSHORT, -EPROTO,
+					rxgk_abort_resp_tok_short);
+
+	_debug("KVNO %u", kvno);
+	_debug("ENC  %u", enctype);
+	_debug("TLEN %zu", ticket_len);
+
+	server_key = rxrpc_look_up_server_security(conn, skb, kvno, enctype);
+	if (IS_ERR(server_key))
+		goto cant_get_server_key;
+
+	down_read(&server_key->sem);
+	server_secret = (const void *)&server_key->payload.data[2];
+	token_enc = rxgk_set_up_token_cipher(server_secret, enctype, GFP_NOFS);
+	up_read(&server_key->sem);
+	key_put(server_key);
+	if (IS_ERR(token_enc)) {
+		ret = PTR_ERR(token_enc);
+		goto cant_get_token;
+	}
+
+	/* We can now decrypt and parse the token/ticket.  This allows us to
+	 * gain access to K0, from which we can derive the transport key and
+	 * thence decode the authenticator.
+	 */
+	ret = rxgk_decrypt_skb(token_enc, skb, &ticket_offset, &ticket_len);
+	crypto_free_aead(token_enc);
+	if (ret < 0)
+		return rxrpc_abort_conn(conn, skb, RXGK_INCONSISTENCY, ret,
+					rxgk_abort_resp_tok_dec);
+
+	ret = conn->security->default_decode_ticket(conn, skb, ticket_offset,
+						    ticket_len, _key);
+	if (ret < 0)
+		goto cant_get_token;
+
+	_leave(" = 0");
+	return ret;
+
+cant_get_server_key:
+	ret = PTR_ERR(server_key);
+	switch (ret) {
+	case -ENOMEM:
+		goto temporary_error;
+	case -ENOKEY:
+	case -EKEYREJECTED:
+	case -EKEYEXPIRED:
+	case -EKEYREVOKED:
+	case -EPERM:
+		return rxrpc_abort_conn(conn, skb, RXGK_BADKEYNO, -EKEYREJECTED,
+					rxgk_abort_resp_tok_nokey);
+	default:
+		return rxrpc_abort_conn(conn, skb, RXGK_NOTAUTH, -EKEYREJECTED,
+					rxgk_abort_resp_tok_keyerr);
+	}
+
+cant_get_token:
+	switch (ret) {
+	case -ENOMEM:
+		goto temporary_error;
+	case -EINVAL:
+		return rxrpc_abort_conn(conn, skb, RXGK_NOTAUTH, -EKEYREJECTED,
+					rxgk_abort_resp_tok_internal_error);
+	case -ENOPKG:
+		return rxrpc_abort_conn(conn, skb, RXGK_BADETYPE, -EKEYREJECTED,
+					rxgk_abort_resp_tok_nopkg);
+	}
+
+temporary_error:
+	/* Ignore the response packet if we got a temporary error such as
+	 * ENOMEM.  We just want to send the challenge again.  Note that we
+	 * also come out this way if the ticket decryption fails.
+	 */
+	return ret;
+}
diff --git a/net/rxrpc/rxgk_common.h b/net/rxrpc/rxgk_common.h
index 84e76fe8e324..9bff844d9557 100644
--- a/net/rxrpc/rxgk_common.h
+++ b/net/rxrpc/rxgk_common.h
@@ -31,6 +31,20 @@ struct rxgk_context {
 	struct crypto_aead	*resp_crypto;	/* Response key */
 };
 
+#define xdr_round_up(x) (round_up((x), sizeof(__be32)))
+
+/*
+ * rxgk_app.c
+ */
+int rxgk_yfs_decode_ticket(struct rxrpc_connection *conn, struct sk_buff *skb,
+			   unsigned int ticket_offset, unsigned int ticket_len,
+			   struct key **_key);
+int rxgk_decrypt_skb(struct crypto_aead *token_enc, struct sk_buff *skb,
+		     size_t *_offset, size_t *_len);
+int rxgk_extract_token(struct rxrpc_connection *conn, struct sk_buff *skb,
+		       unsigned int token_offset, unsigned int token_len,
+		       struct key **_key);
+
 /*
  * rxgk_kdf.c
  */
diff --git a/net/rxrpc/rxkad.c b/net/rxrpc/rxkad.c
index 62b09d23ec08..773405b55392 100644
--- a/net/rxrpc/rxkad.c
+++ b/net/rxrpc/rxkad.c
@@ -177,8 +177,10 @@ static struct rxrpc_txbuf *rxkad_alloc_txbuf(struct rxrpc_call *call, size_t rem
 	if (!txb)
 		return NULL;
 
-	txb->offset += shdr;
-	txb->space = part;
+	txb->crypto_header	= 0;
+	txb->sec_header		= shdr;
+	txb->offset		+= shdr;
+	txb->space		= part;
 	return txb;
 }
 
diff --git a/net/rxrpc/security.c b/net/rxrpc/security.c
index 9784adc8f275..078d91a6b77f 100644
--- a/net/rxrpc/security.c
+++ b/net/rxrpc/security.c
@@ -20,6 +20,9 @@ static const struct rxrpc_security *rxrpc_security_types[] = {
 #ifdef CONFIG_RXKAD
 	[RXRPC_SECURITY_RXKAD]	= &rxkad,
 #endif
+#ifdef CONFIG_RXGK
+	[RXRPC_SECURITY_YFS_RXGK] = &rxgk_yfs,
+#endif
 };
 
 int __init rxrpc_init_security(void)



From dhowells at redhat.com  Thu Jan  9 17:03:10 2025
From: dhowells at redhat.com (David Howells)
Date: Fri, 10 Jan 2025 01:03:10 +0000
Subject: [RFC PATCH 8/8] rxrpc: rxgk: Implement connection rekeying
In-Reply-To: <20250110010313.1471063-1-dhowells@redhat.com>
References: <20250110010313.1471063-1-dhowells@redhat.com>
Message-ID: <20250110010313.1471063-9-dhowells@redhat.com>

Implement rekeying of connections with the RxGK security class.  This
involves regenerating the keys with a different key number as part of the
input data after a certain amount of time or a certain amount of bytes
encrypted.  Rekeying may be triggered by either end.

The LSW of the key number is inserted into the security-specific field in
the RX header, and we try and expand it to 32-bits to make it last longer.

Signed-off-by: David Howells <dhowells at redhat.com>
cc: Herbert Xu <herbert at gondor.apana.org.au>
cc: "David S. Miller" <davem at davemloft.net>
cc: Chuck Lever <chuck.lever at oracle.com>
cc: Marc Dionne <marc.dionne at auristor.com>
cc: Eric Dumazet <edumazet at google.com>
cc: Jakub Kicinski <kuba at kernel.org>
cc: Paolo Abeni <pabeni at redhat.com>
cc: Simon Horman <horms at kernel.org>
cc: linux-afs at lists.infradead.org
cc: linux-nfs at vger.kernel.org
cc: linux-crypto at vger.kernel.org
cc: netdev at vger.kernel.org
---
 net/rxrpc/ar-internal.h |   5 +-
 net/rxrpc/conn_object.c |   1 +
 net/rxrpc/rxgk.c        | 156 ++++++++++++++++++++++++++++++++++++++--
 3 files changed, 155 insertions(+), 7 deletions(-)

diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h
index 736dc6ea20ac..e00f3b0edc98 100644
--- a/net/rxrpc/ar-internal.h
+++ b/net/rxrpc/ar-internal.h
@@ -533,11 +533,14 @@ struct rxrpc_connection {
 			u32	nonce;		/* response re-use preventer */
 		} rxkad;
 		struct {
-			struct rxgk_context *keys[1];
+			struct rxgk_context *keys[4]; /* (Re-)keying buffer */
 			u64	start_time;	/* The start time for TK derivation */
 			u8	nonce[20];	/* Response re-use preventer */
+			u32	key_number;	/* Current key number */
 		} rxgk;
 	};
+	rwlock_t		security_use_lock; /* Security use/modification lock */
+
 	unsigned long		flags;
 	unsigned long		events;
 	unsigned long		idle_timestamp;	/* Time at which last became idle */
diff --git a/net/rxrpc/conn_object.c b/net/rxrpc/conn_object.c
index 7eba4d7d9a38..56459b00266d 100644
--- a/net/rxrpc/conn_object.c
+++ b/net/rxrpc/conn_object.c
@@ -72,6 +72,7 @@ struct rxrpc_connection *rxrpc_alloc_connection(struct rxrpc_net *rxnet,
 		skb_queue_head_init(&conn->rx_queue);
 		conn->rxnet = rxnet;
 		conn->security = &rxrpc_no_security;
+		rwlock_init(&conn->security_use_lock);
 		spin_lock_init(&conn->state_lock);
 		conn->debug_id = atomic_inc_return(&rxrpc_debug_id);
 		conn->idle_timestamp = jiffies;
diff --git a/net/rxrpc/rxgk.c b/net/rxrpc/rxgk.c
index 7344f19b8ae2..c278a242f855 100644
--- a/net/rxrpc/rxgk.c
+++ b/net/rxrpc/rxgk.c
@@ -76,11 +76,153 @@ static void rxgk_describe_server_key(const struct key *key, struct seq_file *m)
 		seq_printf(m, ": %s", krb5->name);
 }
 
+/*
+ * Handle rekeying the connection when we see our limits overrun or when the
+ * far side decided to rekey.
+ *
+ * Returns a ref on the context if successful or -ESTALE if the key is out of
+ * date.
+ */
+static struct rxgk_context *rxgk_rekey(struct rxrpc_connection *conn,
+				       const u16 *specific_key_number)
+{
+	struct rxgk_context *gk, *dead = NULL;
+	unsigned int key_number, current_key, mask = ARRAY_SIZE(conn->rxgk.keys) - 1;
+	bool crank = false;
+
+	_enter("%d", specific_key_number ? *specific_key_number : -1);
+
+	mutex_lock(&conn->security_lock);
+
+	current_key = conn->rxgk.key_number;
+	if (!specific_key_number) {
+		key_number = current_key;
+	} else {
+		if (*specific_key_number == (u16)current_key)
+			key_number = current_key;
+		else if (*specific_key_number == (u16)(current_key - 1))
+			key_number = current_key - 1;
+		else if (*specific_key_number == (u16)(current_key + 1))
+			goto crank_window;
+		else
+			goto bad_key;
+	}
+
+	gk = conn->rxgk.keys[key_number & mask];
+	if (!gk)
+		goto generate_key;
+	if (!specific_key_number &&
+	    test_bit(RXGK_TK_NEEDS_REKEY, &gk->flags))
+		goto crank_window;
+
+grab:
+	refcount_inc(&gk->usage);
+	mutex_unlock(&conn->security_lock);
+	rxgk_put(dead);
+	return gk;
+
+crank_window:
+	if (current_key == UINT_MAX)
+		goto bad_key;
+	if (current_key + 1 == UINT_MAX)
+		set_bit(RXRPC_CONN_DONT_REUSE, &conn->flags);
+
+	key_number = current_key + 1;
+	if (WARN_ON(conn->rxgk.keys[key_number & mask]))
+		goto bad_key;
+	crank = true;
+
+generate_key:
+	gk = conn->rxgk.keys[current_key & mask];
+	gk = rxgk_generate_transport_key(conn, gk->key, key_number, GFP_NOFS);
+	if (IS_ERR(gk)) {
+		mutex_unlock(&conn->security_lock);
+		return gk;
+	}
+
+	write_lock(&conn->security_use_lock);
+	if (crank) {
+		current_key++;
+		conn->rxgk.key_number = current_key;
+		dead = conn->rxgk.keys[(current_key - 2) & mask];
+		conn->rxgk.keys[(current_key - 2) & mask] = NULL;
+	}
+	conn->rxgk.keys[current_key & mask] = gk;
+	write_unlock(&conn->security_use_lock);
+	goto grab;
+
+bad_key:
+	mutex_unlock(&conn->security_lock);
+	return ERR_PTR(-ESTALE);
+}
+
+/*
+ * Get the specified keying context.
+ *
+ * Returns a ref on the context if successful or -ESTALE if the key is out of
+ * date.
+ */
 static struct rxgk_context *rxgk_get_key(struct rxrpc_connection *conn,
-					 u16 *specific_key_number)
+					 const u16 *specific_key_number)
 {
-	refcount_inc(&conn->rxgk.keys[0]->usage);
-	return conn->rxgk.keys[0];
+	struct rxgk_context *gk;
+	unsigned int key_number, current_key, mask = ARRAY_SIZE(conn->rxgk.keys) - 1;
+
+	_enter("{%u},%d",
+	       conn->rxgk.key_number, specific_key_number ? *specific_key_number : -1);
+
+	read_lock(&conn->security_use_lock);
+
+	current_key = conn->rxgk.key_number;
+	if (!specific_key_number) {
+		key_number = current_key;
+	} else {
+		/* Only the bottom 16 bits of the key number are exposed in the
+		 * header, so we try and keep the upper 16 bits in step.  The
+		 * whole 32 bits are used to generate the TK.
+		 */
+		if (*specific_key_number == (u16)current_key)
+			key_number = current_key;
+		else if (*specific_key_number == (u16)(current_key - 1))
+			key_number = current_key - 1;
+		else if (*specific_key_number == (u16)(current_key + 1))
+			goto rekey;
+		else
+			goto bad_key;
+	}
+
+	gk = conn->rxgk.keys[key_number & mask];
+	if (!gk)
+		goto slow_path;
+	if (!specific_key_number &&
+	    key_number < UINT_MAX) {
+		if (time_after(jiffies, gk->expiry) ||
+		    gk->bytes_remaining < 0) {
+			set_bit(RXGK_TK_NEEDS_REKEY, &gk->flags);
+			goto slow_path;
+		}
+
+		if (test_bit(RXGK_TK_NEEDS_REKEY, &gk->flags))
+			goto slow_path;
+	}
+
+	refcount_inc(&gk->usage);
+	read_unlock(&conn->security_use_lock);
+	return gk;
+
+rekey:
+	_debug("rekey");
+	if (current_key == UINT_MAX)
+		goto bad_key;
+	gk = conn->rxgk.keys[current_key & mask];
+	if (gk)
+		set_bit(RXGK_TK_NEEDS_REKEY, &gk->flags);
+slow_path:
+	read_unlock(&conn->security_use_lock);
+	return rxgk_rekey(conn, specific_key_number);
+bad_key:
+	read_unlock(&conn->security_use_lock);
+	return ERR_PTR(-ESTALE);
 }
 
 /*
@@ -92,7 +234,8 @@ static int rxgk_init_connection_security(struct rxrpc_connection *conn,
 	struct rxgk_context *gk;
 	int ret;
 
-	_enter("{%d},{%x}", conn->debug_id, key_serial(conn->key));
+	_enter("{%d,%u},{%x}",
+	       conn->debug_id, conn->rxgk.key_number, key_serial(conn->key));
 
 	conn->security_ix = token->security_index;
 	conn->security_level = token->rxgk->level;
@@ -102,10 +245,11 @@ static int rxgk_init_connection_security(struct rxrpc_connection *conn,
 		do_div(conn->rxgk.start_time, 100);
 	}
 
-	gk = rxgk_generate_transport_key(conn, token->rxgk, 0, GFP_NOFS);
+	gk = rxgk_generate_transport_key(conn, token->rxgk, conn->rxgk.key_number,
+					 GFP_NOFS);
 	if (IS_ERR(gk))
 		return PTR_ERR(gk);
-	conn->rxgk.keys[0] = gk;
+	conn->rxgk.keys[gk->key_number & 3] = gk;
 
 	switch (conn->security_level) {
 	case RXRPC_SECURITY_PLAIN:



From ebiggers at kernel.org  Thu Jan  9 21:50:58 2025
From: ebiggers at kernel.org (Eric Biggers)
Date: Thu, 9 Jan 2025 21:50:58 -0800
Subject: [RFC PATCH 2/8] crypto/krb5: Provide Kerberos 5 crypto through
 AEAD API
In-Reply-To: <20250110010313.1471063-3-dhowells@redhat.com>
References: <20250110010313.1471063-1-dhowells@redhat.com>
 <20250110010313.1471063-3-dhowells@redhat.com>
Message-ID: <20250110055058.GA63811@sol.localdomain>

On Fri, Jan 10, 2025 at 01:03:04AM +0000, David Howells wrote:
> Use the AEAD crypto API to provide Kerberos 5 crypto, plus some
> supplementary library functions that lie outside of the AEAD API.
> 
> The crypto algorithms only perform the actual crypto operations; they do
> not do any laying out of the message and nor do they insert any metadata or
> padding.  Everything is done by dead-reckoning as the AEAD API does not
> provide a useful way to pass the extra parameters required.
> 
> When setting the key on a crypto algorithm, setkey takes a composite
> structure consisting of an indication of the mode of transformation to be
> applied to the message (checksum only or full encryption); the usage type
> to be used in deriving the keys; an indicator indicating what key is being
> presented (K0 or Kc/Ke+Ki); and the material for those key(s).  Based on
> this, the setkey code allocates and keys the appropriate ciphers and
> hashes.
> 
> When dispatching a request, both checksumming (MIC) and encryption use the
> encrypt and decrypt methods.  A source message, prelaid out with
> confounders or other metadata inserted is provided in the source buffer.
> The cryptolen indicates the amount of source message data, not including
> the trailer after the data (which includes the integrity checksum) and not
> including any associated data.
> 
> Associated data is only used by checksumming encrypt/decrypt.  The
> associated data is added to the checksum hash before the data in the
> message, but does not occupy any part of the output message.
> 
> Authentication tags are not used at all and should cause EINVAL if used (a
> later patch does that).
> 
> For the moment, the kerberos encryption algorithms use separate hash and
> cipher algorithms internally, but should really use dual hash+cipher and
> cipher+hash algorithms if possible to avoid doing these in series.  Offload
> off this may be possible through something like the Intel QAT.

It sounds like a lot of workarounds had to be implemented to fit these protocols
into the crypto_aead API.

It also seems unlikely that there will be other implementations of these
protocols added to the kernel, besides the one you're adding in crypto/krb5/.

Given that, providing this functionality as library functions instead would be
much simpler.  Take a look at how crypto/kdf_sp800108.c works, for example.

- Eric


From vincenc.turek at commetrax.com  Fri Jan 10 00:46:13 2025
From: vincenc.turek at commetrax.com (Vincenc Turek)
Date: Fri, 10 Jan 2025 08:46:13 GMT
Subject: Plastic packaging
Message-ID: <20250110084500-0.1.76.gorj.0.nke3dglki5@commetrax.com>

Hi,

Are you looking for packaging that not only protects your product but also enhances its value in the eyes of your customers?

We manufacture a wide range of bottles, caps, and buckets made of HDPE and PP, designed to meet the demands of industries such as cosmetics, chemicals, and food.

Our packaging stands out for its durability, aesthetic appearance, and the ability to customize colors and closures ? helping to build brand recognition and trust.

Are you interested in solutions that perfectly align with your company's needs?


Best regards
Vincenc Turek


From patryk.wysocki at taskinium.pl  Fri Jan 10 01:00:38 2025
From: patryk.wysocki at taskinium.pl (Patryk Wysocki)
Date: Fri, 10 Jan 2025 09:00:38 GMT
Subject: Zapytanie ofertowe 
Message-ID: <20250110084501-0.1.d.1qpm.0.s2n27a1375@taskinium.pl>

Dzie? dobry,

Pozwoli?em sobie na kontakt, poniewa? jestem zainteresowany weryfikacj? mo?liwo?ci nawi?zania wsp??pracy.

Wspieramy firmy w pozyskiwaniu nowych klient?w biznesowych.

Czy mo?emy porozmawia? w celu przedstawienia szczeg??owych informacji? 


Pozdrawiam
Patryk Wysocki


From dhowells at redhat.com  Thu Jan  9 23:13:48 2025
From: dhowells at redhat.com (David Howells)
Date: Fri, 10 Jan 2025 07:13:48 +0000
Subject: [RFC PATCH 2/8] crypto/krb5: Provide Kerberos 5 crypto through AEAD API
In-Reply-To: <20250110055058.GA63811@sol.localdomain>
References: <20250110055058.GA63811@sol.localdomain> <20250110010313.1471063-1-dhowells@redhat.com> <20250110010313.1471063-3-dhowells@redhat.com>
Message-ID: <1478993.1736493228@warthog.procyon.org.uk>

Eric Biggers <ebiggers at kernel.org> wrote:

> It sounds like a lot of workarounds had to be implemented to fit these
> protocols into the crypto_aead API.
> 
> It also seems unlikely that there will be other implementations of these
> protocols added to the kernel, besides the one you're adding in crypto/krb5/.
> 
> Given that, providing this functionality as library functions instead would be
> much simpler.  Take a look at how crypto/kdf_sp800108.c works, for example.

Yes.  That's how I did my first implementation.  I basically took the code
from net/sunrpc/auth_gss/ and made it more generic.  Herbert wants it done
this way, however.  :-/

David



From ardb at kernel.org  Fri Jan 10 01:47:54 2025
From: ardb at kernel.org (Ard Biesheuvel)
Date: Fri, 10 Jan 2025 10:47:54 +0100
Subject: [RFC PATCH 2/8] crypto/krb5: Provide Kerberos 5 crypto through
 AEAD API
In-Reply-To: <1478993.1736493228@warthog.procyon.org.uk>
References: <20250110010313.1471063-1-dhowells@redhat.com> <20250110010313.1471063-3-dhowells@redhat.com>
 <20250110055058.GA63811@sol.localdomain> <1478993.1736493228@warthog.procyon.org.uk>
Message-ID: <CAMj1kXE2mhXJaa9uq==Xki3On9ZKYY+KV-oH0ednqWC6b9BTYw@mail.gmail.com>

On Fri, 10 Jan 2025 at 08:14, David Howells <dhowells at redhat.com> wrote:
>
> Eric Biggers <ebiggers at kernel.org> wrote:
>
> > It sounds like a lot of workarounds had to be implemented to fit these
> > protocols into the crypto_aead API.
> >
> > It also seems unlikely that there will be other implementations of these
> > protocols added to the kernel, besides the one you're adding in crypto/krb5/.
> >
> > Given that, providing this functionality as library functions instead would be
> > much simpler.  Take a look at how crypto/kdf_sp800108.c works, for example.
>
> Yes.  That's how I did my first implementation.  I basically took the code
> from net/sunrpc/auth_gss/ and made it more generic.  Herbert wants it done
> this way, however.  :-/
>

What is the reason for shoehorning any of this into the crypto API?

I agree with Eric here: it seems both the user (Kerberos) and the
crypto API are worse off here, due to mutual API incompatibilities
that seem rather fundamental.

Are you anticipating other, accelerated implementations of the
combined algorithms? Isn't it enough to rely on the existing Camellia
and AES code? Mentioning 'something like the Intel QAT' doesn't
suggest you have something specific in mind.

Also, this patch is rather big and therefore hard to review.


From herbert at gondor.apana.org.au  Fri Jan 10 01:48:36 2025
From: herbert at gondor.apana.org.au (Herbert Xu)
Date: Fri, 10 Jan 2025 17:48:36 +0800
Subject: [RFC PATCH 2/8] crypto/krb5: Provide Kerberos 5 crypto through
 AEAD API
In-Reply-To: <20250110010313.1471063-3-dhowells@redhat.com>
References: <20250110010313.1471063-1-dhowells@redhat.com>
 <20250110010313.1471063-3-dhowells@redhat.com>
Message-ID: <Z4Ds9NBiXUti-idl@gondor.apana.org.au>

On Fri, Jan 10, 2025 at 01:03:04AM +0000, David Howells wrote:
>
> +		.etype			= KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128,
> +		.ctype			= KRB5_CKSUMTYPE_HMAC_SHA256_128_AES128,
> +		.name			= "aes128-cts-hmac-sha256-128",
> +		.encrypt_name		= "cts(cbc(aes))",
> +		.cksum_name		= "hmac(sha256)",
> +		.hash_name		= "sha256",
> +		.key_bytes		= 16,
> +		.key_len		= 16,
> +		.Kc_len			= 16,
> +		.Ke_len			= 16,
> +		.Ki_len			= 16,
> +		.block_len		= 16,
> +		.conf_len		= 16,
> +		.cksum_len		= 16,
> +		.hash_len		= 20,
> +		.prf_len		= 32,
> +		.keyed_cksum		= true,
> +		.random_to_key		= NULL, /* Identity */
> +		.profile		= &rfc8009_crypto_profile,
> +
> +		.aead.setkey		= krb5_setkey,
> +		.aead.setauthsize	= NULL,
> +		.aead.encrypt		= rfc8009_aead_encrypt,
> +		.aead.decrypt		= rfc8009_aead_decrypt,

rfc8009 is basically the same as authenc.  So rather than being an
AEAD algorithm it should really be an AEAD template which takes a
cipher and and a hash as its parameters.

In fact, you could probably use authenc directly.

rfc3691 on the other hand is slightly different from authenc in that
the integrity is computed on the plain text.

Cheers,
-- 
Email: Herbert Xu <herbert at gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt


From herbert at gondor.apana.org.au  Fri Jan 10 02:02:28 2025
From: herbert at gondor.apana.org.au (Herbert Xu)
Date: Fri, 10 Jan 2025 18:02:28 +0800
Subject: [RFC PATCH 2/8] crypto/krb5: Provide Kerberos 5 crypto through
 AEAD API
In-Reply-To: <20250110010313.1471063-3-dhowells@redhat.com>
References: <20250110010313.1471063-1-dhowells@redhat.com>
 <20250110010313.1471063-3-dhowells@redhat.com>
Message-ID: <Z4DwNPgLFcfy6jdl@gondor.apana.org.au>

On Fri, Jan 10, 2025 at 01:03:04AM +0000, David Howells wrote:
>
> Authentication tags are not used at all and should cause EINVAL if used (a
> later patch does that).

What do you mean by this? The authentication tag is the checksum
that you're referring to and you appear to be using it in the rfc8009
encrypt/decrypt functions.

> For the moment, the kerberos encryption algorithms use separate hash and
> cipher algorithms internally, but should really use dual hash+cipher and
> cipher+hash algorithms if possible to avoid doing these in series.  Offload
> off this may be possible through something like the Intel QAT.

Please elaborate on what you mean by this.  For IPsec, the main
benefit with reframing cbc(aes)+hmac as aead is having a single
code-path that supports both types of algorithms.

So does your use-case support both standard AEAD algorithms such
as GCM as well as these legacy algorithms?

Cheers,
-- 
Email: Herbert Xu <herbert at gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt


From dhowells at redhat.com  Fri Jan 10 02:26:38 2025
From: dhowells at redhat.com (David Howells)
Date: Fri, 10 Jan 2025 10:26:38 +0000
Subject: [RFC PATCH 2/8] crypto/krb5: Provide Kerberos 5 crypto through AEAD API
In-Reply-To: <Z4Ds9NBiXUti-idl@gondor.apana.org.au>
References: <Z4Ds9NBiXUti-idl@gondor.apana.org.au> <20250110010313.1471063-1-dhowells@redhat.com> <20250110010313.1471063-3-dhowells@redhat.com>
Message-ID: <1485676.1736504798@warthog.procyon.org.uk>

Herbert Xu <herbert at gondor.apana.org.au> wrote:

> rfc8009 is basically the same as authenc.  So rather than being an
> AEAD algorithm it should really be an AEAD template which takes a
> cipher and and a hash as its parameters.

That's only half true.  If it's acting in checksum mode then it's not an
authenc() algo.

> In fact, you could probably use authenc directly.

However the point of having a library is to abstract those details from the
callers.  You wanted me to rewrite the library as AEAD algorithms, which I
have done as far as I can.  This makes the object for each kerberos enctype
look the same from the PoV of the clients.

I have plans to make the kerberos AEAD use an authenc behind the scenes rather
than a cipher plus hash where appropriate as a future evolution, but the
optimised authenc drivers (QAT for example) that I can find don't appear to
support CTS.

So I'm not sure what it is you were envisioning.

David



From herbert at gondor.apana.org.au  Fri Jan 10 02:30:16 2025
From: herbert at gondor.apana.org.au (Herbert Xu)
Date: Fri, 10 Jan 2025 18:30:16 +0800
Subject: [RFC PATCH 2/8] crypto/krb5: Provide Kerberos 5 crypto through
 AEAD API
In-Reply-To: <1485676.1736504798@warthog.procyon.org.uk>
References: <Z4Ds9NBiXUti-idl@gondor.apana.org.au>
 <20250110010313.1471063-1-dhowells@redhat.com>
 <20250110010313.1471063-3-dhowells@redhat.com>
 <1485676.1736504798@warthog.procyon.org.uk>
Message-ID: <Z4D2uC-kcSzQJS-H@gondor.apana.org.au>

On Fri, Jan 10, 2025 at 10:26:38AM +0000, David Howells wrote:
>
> However the point of having a library is to abstract those details from the
> callers.  You wanted me to rewrite the library as AEAD algorithms, which I
> have done as far as I can.  This makes the object for each kerberos enctype
> look the same from the PoV of the clients.

I think there is some misunderstanding here.  For a library outside
of the Crypto API you can do whatever you want.

I only suggested AEAD because I thought you wanted to bring this within
the Crypto API.

Cheers,
-- 
Email: Herbert Xu <herbert at gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt


From dhowells at redhat.com  Fri Jan 10 02:39:27 2025
From: dhowells at redhat.com (David Howells)
Date: Fri, 10 Jan 2025 10:39:27 +0000
Subject: [RFC PATCH 2/8] crypto/krb5: Provide Kerberos 5 crypto through AEAD API
In-Reply-To: <Z4DwNPgLFcfy6jdl@gondor.apana.org.au>
References: <Z4DwNPgLFcfy6jdl@gondor.apana.org.au> <20250110010313.1471063-1-dhowells@redhat.com> <20250110010313.1471063-3-dhowells@redhat.com>
Message-ID: <1486113.1736505567@warthog.procyon.org.uk>

Herbert Xu <herbert at gondor.apana.org.au> wrote:

> > Authentication tags are not used at all and should cause EINVAL if used (a
> > later patch does that).
> 
> What do you mean by this? The authentication tag is the checksum
> that you're referring to and you appear to be using it in the rfc8009
> encrypt/decrypt functions.

Is it?  That's entirely unclear.  The algorithm should deal with inserting the
checksum in the appropriate place.  The caller should not need to know about
that or where the checksum is or about extra bits of metadata that may need to
be inserted (as I think the extra gssapi layer does for sunrpc).

One of the reason the library has a number of layout functions is to handle
that stuff transparently.  Unfortunately, I couldn't make it work in the AEAD
interface.  The previous library implementation was better in that regard.

> > For the moment, the kerberos encryption algorithms use separate hash and
> > cipher algorithms internally, but should really use dual hash+cipher and
> > cipher+hash algorithms if possible to avoid doing these in series.  Offload
> > off this may be possible through something like the Intel QAT.
> 
> Please elaborate on what you mean by this.  For IPsec, the main
> benefit with reframing cbc(aes)+hmac as aead is having a single
> code-path that supports both types of algorithms.

By "dual" I mean, for example, a piece of code that does the cipher and the
hash concurrently.  I think it may be possible to do this using x86 AES and
SHA instructions - if there are sufficient registers.  What I want to do is
avoid having to call a cipher and a hash sequentially.  It appears that the
Intel QAT can actually do this with authenc combos - but the one I have
doesn't offer CTS(CBC(AES)) but only CBC(AES).

> So does your use-case support both standard AEAD algorithms such
> as GCM as well as these legacy algorithms?

At the moment AFS's rxgk does not support GCM.  The same goes for sunrpc in
the kernel.

David



From herbert at gondor.apana.org.au  Fri Jan 10 02:42:15 2025
From: herbert at gondor.apana.org.au (Herbert Xu)
Date: Fri, 10 Jan 2025 18:42:15 +0800
Subject: [RFC PATCH 2/8] crypto/krb5: Provide Kerberos 5 crypto through
 AEAD API
In-Reply-To: <1486113.1736505567@warthog.procyon.org.uk>
References: <Z4DwNPgLFcfy6jdl@gondor.apana.org.au>
 <20250110010313.1471063-1-dhowells@redhat.com>
 <20250110010313.1471063-3-dhowells@redhat.com>
 <1486113.1736505567@warthog.procyon.org.uk>
Message-ID: <Z4D5h_KBvxwmrdQq@gondor.apana.org.au>

On Fri, Jan 10, 2025 at 10:39:27AM +0000, David Howells wrote:
>
> Is it?  That's entirely unclear.  The algorithm should deal with inserting the
> checksum in the appropriate place.  The caller should not need to know about
> that or where the checksum is or about extra bits of metadata that may need to
> be inserted (as I think the extra gssapi layer does for sunrpc).

The AEAD interface does not dictate where the authentication
tag is.  Most algorithms put it at the end, but it really could
be anywhere.

Cheers,
-- 
Email: Herbert Xu <herbert at gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt


From dhowells at redhat.com  Fri Jan 10 03:09:49 2025
From: dhowells at redhat.com (David Howells)
Date: Fri, 10 Jan 2025 11:09:49 +0000
Subject: [RFC PATCH 2/8] crypto/krb5: Provide Kerberos 5 crypto through AEAD API
In-Reply-To: <Z4D2uC-kcSzQJS-H@gondor.apana.org.au>
References: <Z4D2uC-kcSzQJS-H@gondor.apana.org.au> <Z4Ds9NBiXUti-idl@gondor.apana.org.au> <20250110010313.1471063-1-dhowells@redhat.com> <20250110010313.1471063-3-dhowells@redhat.com> <1485676.1736504798@warthog.procyon.org.uk>
Message-ID: <1488634.1736507389@warthog.procyon.org.uk>

Herbert Xu <herbert at gondor.apana.org.au> wrote:

> On Fri, Jan 10, 2025 at 10:26:38AM +0000, David Howells wrote:
> >
> > However the point of having a library is to abstract those details from the
> > callers.  You wanted me to rewrite the library as AEAD algorithms, which I
> > have done as far as I can.  This makes the object for each kerberos enctype
> > look the same from the PoV of the clients.
> 
> I think there is some misunderstanding here.  For a library outside
> of the Crypto API you can do whatever you want.
> 
> I only suggested AEAD because I thought you wanted to bring this within
> the Crypto API.

Not precisely.  What I (and Chuck when I discussed it with him) were thinking
is that the kerberos crypto stuff probably belongs in the crypto/ *directory*
rather than in the net/ directory - but not necessarily as part of the crypto
API.  It mediates use of the crypto API on the part of its users (probably
just sunrpc and rxrpc's rxgk).

That said, I kind of like the implementation of the pure crypto part as AEAD
crypto algorithms as it provides a number of advantages:

 (1) The client can be given a single AEAD object to use for each usage and
     call the encrypt and decrypt on that directly, no matter what enctype or
     mode of operation it is doing.

     Of course, it's not quite so simple that I can just share the code for
     encrypt-mode and checksum-mode at the client level (eg. rxgk).  In the
     former, some metadata is placed in the message; in the latter it's just
     added into the hash.

 (2) The AEAD object looks after inserting the checksum into the right place
     for the enctype, which means the client doesn't have to do that and could
     therefore more easily asynchronise it through the crypto API.

 (3) Since these do just the crypto and not the laying out, it may be feasible
     to substitute the AES2 encrypt-mode kerberos AEAD driver with an
     authenc() AEAD object.

 (4) The possibility exists of providing optimised drivers to directly
     substitute the kerberos AEAD algorithms.

David



From dhowells at redhat.com  Fri Jan 10 06:33:36 2025
From: dhowells at redhat.com (David Howells)
Date: Fri, 10 Jan 2025 14:33:36 +0000
Subject: [RFC PATCH 2/8] crypto/krb5: Provide Kerberos 5 crypto through AEAD API
In-Reply-To: <CAMj1kXE2mhXJaa9uq==Xki3On9ZKYY+KV-oH0ednqWC6b9BTYw@mail.gmail.com>
References: <CAMj1kXE2mhXJaa9uq==Xki3On9ZKYY+KV-oH0ednqWC6b9BTYw@mail.gmail.com> <20250110010313.1471063-1-dhowells@redhat.com> <20250110010313.1471063-3-dhowells@redhat.com> <20250110055058.GA63811@sol.localdomain> <1478993.1736493228@warthog.procyon.org.uk>
Message-ID: <1494600.1736519616@warthog.procyon.org.uk>

Ard Biesheuvel <ardb at kernel.org> wrote:

> What is the reason for shoehorning any of this into the crypto API?

I was under the impression that that was what Herbert wanted.

> I agree with Eric here: it seems both the user (Kerberos) and the
> crypto API are worse off here, due to mutual API incompatibilities
> that seem rather fundamental.

My original take on this was to take the sunrpc code and turn it into a
library, placing that library in the crypto/ directory:

	https://lore.kernel.org/linux-crypto/160518586534.2277919.14475638653680231924.stgit at warthog.procyon.org.uk/

The crypto/ dir seems the right home for it (and not net/ or lib/), but the
way it's implemented here, it's a user of the crypto API, but does not itself
implement it.

That said, it would be convenient if if *could* be part of the crypto API in
some way.  As I outlined in one of my responses to Herbert, there are a number
of advantages to doing that.

> Are you anticipating other, accelerated implementations of the
> combined algorithms?

I think one can be done with x86 AES and SHA instructions provided there are
sufficient registers.

> Isn't it enough to rely on the existing Camellia and AES code?

The problem is that you have to do *two* crypto operations - and that it may
not be possible to parallellise them.  With AES+SHA1 and Camellia, they can be
parallellised as both sides work on the plaintext; but with the AES+SHA2, the
encryption is done and then the *encrypted* output is checksummed.

Note that "parallellising" might mean launching an async hash request and an
async skcipher request and then waiting for both to finish.  This can't,
however, be done unless the output buffer is separate from the input buffer.

> Mentioning 'something like the Intel QAT' doesn't suggest you have something
> specific in mind.

I have an Intel QAT card, and under some circumstances I could offload the
crypto to it...  But the only operations the driver currently makes available
are:

	authenc(hmac(sha1),cbc(aes))
	authenc(hmac(sha256),cbc(aes))
	authenc(hmac(sha512),cbc(aes))

The first one can't be used for kerberos's aes128-cts-hmac-sha1-96 as it
hashes the ciphertext, not the plain text.  I don't have anything that uses
the third.  The second is a possibility.  I think that could probably do
aes128-cts-hmac-sha256-128.

Now, it's possible that the QAT device range can do more than the driver
offers.  I'm presuming that the driver offers what IPsec wants to support.
Also, waving these ideas in front of Intel devs might expand the range of what
future QATs can do ;-)

Mostly, though, by "something like" I was just offering the possibility that
other architectures or crypto cards may also offer usable services - but I
haven't investigated.

> Also, this patch is rather big and therefore hard to review.

Yeah.  Mostly I wanted to wave it in front of Herbert before expending the
effort to slice it up.  Sadly, it doesn't seem that what I came up with is
what he wanted.

David



From sashal at kernel.org  Mon Jan 13 10:34:24 2025
From: sashal at kernel.org (Sasha Levin)
Date: Mon, 13 Jan 2025 13:34:24 -0500
Subject: [PATCH AUTOSEL 6.12 19/20] afs: Fix merge preference rule failure condition
In-Reply-To: <20250113183425.1783715-1-sashal@kernel.org>
References: <20250113183425.1783715-1-sashal@kernel.org>
Message-ID: <20250113183425.1783715-19-sashal@kernel.org>

From: Lizhi Xu <lizhi.xu at windriver.com>

[ Upstream commit 17a4fde81d3a7478d97d15304a6d61094a10c2e3 ]

syzbot reported a lock held when returning to userspace[1].  This is
because if argc is less than 0 and the function returns directly, the held
inode lock is not released.

Fix this by store the error in ret and jump to done to clean up instead of
returning directly.

[dh: Modified Lizhi Xu's original patch to make it honour the error code
from afs_split_string()]

[1]
WARNING: lock held when returning to user space!
6.13.0-rc3-syzkaller-00209-g499551201b5f #0 Not tainted
------------------------------------------------
syz-executor133/5823 is leaving the kernel with locks still held!
1 lock held by syz-executor133/5823:
 #0: ffff888071cffc00 (&sb->s_type->i_mutex_key#9){++++}-{4:4}, at: inode_lock include/linux/fs.h:818 [inline]
 #0: ffff888071cffc00 (&sb->s_type->i_mutex_key#9){++++}-{4:4}, at: afs_proc_addr_prefs_write+0x2bb/0x14e0 fs/afs/addr_prefs.c:388

Reported-by: syzbot+76f33569875eb708e575 at syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=76f33569875eb708e575
Signed-off-by: Lizhi Xu <lizhi.xu at windriver.com>
Signed-off-by: David Howells <dhowells at redhat.com>
Link: https://lore.kernel.org/r/20241226012616.2348907-1-lizhi.xu at windriver.com/
Link: https://lore.kernel.org/r/529850.1736261552 at warthog.procyon.org.uk
Tested-by: syzbot+76f33569875eb708e575 at syzkaller.appspotmail.com
cc: Marc Dionne <marc.dionne at auristor.com>
cc: linux-afs at lists.infradead.org
Signed-off-by: Christian Brauner <brauner at kernel.org>
Signed-off-by: Sasha Levin <sashal at kernel.org>
---
 fs/afs/addr_prefs.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/fs/afs/addr_prefs.c b/fs/afs/addr_prefs.c
index a189ff8a5034..c0384201b8fe 100644
--- a/fs/afs/addr_prefs.c
+++ b/fs/afs/addr_prefs.c
@@ -413,8 +413,10 @@ int afs_proc_addr_prefs_write(struct file *file, char *buf, size_t size)
 
 	do {
 		argc = afs_split_string(&buf, argv, ARRAY_SIZE(argv));
-		if (argc < 0)
-			return argc;
+		if (argc < 0) {
+			ret = argc;
+			goto done;
+		}
 		if (argc < 2)
 			goto inval;
 
-- 
2.39.5



From dhowells at redhat.com  Tue Jan 14 06:46:03 2025
From: dhowells at redhat.com (David Howells)
Date: Tue, 14 Jan 2025 14:46:03 +0000
Subject: [PATCH] afs: Fix the fallback handling for the YFS.RemoveFile2 RPC call
Message-ID: <109541.1736865963@warthog.procyon.org.uk>

Fix a pair of bugs in the fallback handling for the YFS.RemoveFile2 RPC
call:

 (1) Fix the abort code check to also look for RXGEN_OPCODE.  The lack of
     this masks the second bug.

 (2) call->server is now not used for ordinary filesystem RPC calls that
     have an operation descriptor.  Fix to use call->op->server instead.

Fixes: e49c7b2f6de7 ("afs: Build an abstraction around an "operation" concept")
Signed-off-by: David Howells <dhowells at redhat.com>
cc: Marc Dionne <marc.dionne at auristor.com>
cc: linux-afs at lists.infradead.org
---
 fs/afs/yfsclient.c |    5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/fs/afs/yfsclient.c b/fs/afs/yfsclient.c
index 024227aba4cd..362845f9aaae 100644
--- a/fs/afs/yfsclient.c
+++ b/fs/afs/yfsclient.c
@@ -666,8 +666,9 @@ static int yfs_deliver_fs_remove_file2(struct afs_call *call)
 static void yfs_done_fs_remove_file2(struct afs_call *call)
 {
 	if (call->error == -ECONNABORTED &&
-	    call->abort_code == RX_INVALID_OPERATION) {
-		set_bit(AFS_SERVER_FL_NO_RM2, &call->server->flags);
+	    (call->abort_code == RX_INVALID_OPERATION ||
+	     call->abort_code == RXGEN_OPCODE)) {
+		set_bit(AFS_SERVER_FL_NO_RM2, &call->op->server->flags);
 		call->op->flags |= AFS_OPERATION_DOWNGRADE;
 	}
 }



From jaltman at auristor.com  Fri Jan 10 10:22:23 2025
From: jaltman at auristor.com (Jeffrey E Altman)
Date: Fri, 10 Jan 2025 13:22:23 -0500
Subject: [RFC PATCH 2/8] crypto/krb5: Provide Kerberos 5 crypto through
 AEAD API
In-Reply-To: <Z4DwNPgLFcfy6jdl@gondor.apana.org.au>
References: <20250110010313.1471063-1-dhowells@redhat.com>
 <20250110010313.1471063-3-dhowells@redhat.com>
 <Z4DwNPgLFcfy6jdl@gondor.apana.org.au>
Message-ID: <f506a4fa-ffa2-4258-ae3c-c3cf4568f9a4@auristor.com>

On 1/10/2025 5:02 AM, Herbert Xu wrote:
> So does your use-case support both standard AEAD algorithms such
> as GCM as well as these legacy algorithms?

RXGK is described by 
https://datatracker.ietf.org/doc/draft-wilkinson-afs3-rxgk/.

Any RFC3961 ("Encryption and Checksum Specifications for Kerberos 5") 
framework encryption algorithm can be used with it.

There have been proposals to add AEAD encryption types to RFC3961. For 
example, Luke Howard proposed

https://datatracker.ietf.org/doc/draft-howard-krb-aead/

The Security Considerations section describes the reasons that MIT's 
Kerberos team is reluctant to add AEAD algorithms to RFC3961. The 
primary one being that AEAD algorithms are not safe for all of the 
existing RFC3961 use cases and there is no means of ensuring that AEAD 
encryption types would not be misused.

Requests for the addition of AEAD to RFC3961 have come from both the 
NFSv4 community and those implementing RXGK. Alas, there has been no 
forward progress since the publication of Luke's draft.

Jeffrey Altman

-------------- next part --------------
A non-text attachment was scrubbed...
Name: smime.p7s
Type: application/pkcs7-signature
Size: 4276 bytes
Desc: S/MIME Cryptographic Signature
URL: <http://lists.infradead.org/pipermail/linux-afs/attachments/20250110/2de2270a/attachment-0001.p7s>

From sashal at kernel.org  Mon Jan 13 10:34:11 2025
From: sashal at kernel.org (Sasha Levin)
Date: Mon, 13 Jan 2025 13:34:11 -0500
Subject: [PATCH AUTOSEL 6.12 06/20] kheaders: Ignore silly-rename files
In-Reply-To: <20250113183425.1783715-1-sashal@kernel.org>
References: <20250113183425.1783715-1-sashal@kernel.org>
Message-ID: <20250113183425.1783715-6-sashal@kernel.org>

From: David Howells <dhowells at redhat.com>

[ Upstream commit 973b710b8821c3401ad7a25360c89e94b26884ac ]

Tell tar to ignore silly-rename files (".__afs*" and ".nfs*") when building
the header archive.  These occur when a file that is open is unlinked
locally, but hasn't yet been closed.  Such files are visible to the user
via the getdents() syscall and so programs may want to do things with them.

During the kernel build, such files may be made during the processing of
header files and the cleanup may get deferred by fput() which may result in
tar seeing these files when it reads the directory, but they may have
disappeared by the time it tries to open them, causing tar to fail with an
error.  Further, we don't want to include them in the tarball if they still
exist.

With CONFIG_HEADERS_INSTALL=y, something like the following may be seen:

   find: './kernel/.tmp_cpio_dir/include/dt-bindings/reset/.__afs2080': No such file or directory
   tar: ./include/linux/greybus/.__afs3C95: File removed before we read it

The find warning doesn't seem to cause a problem.

Fix this by telling tar when called from in gen_kheaders.sh to exclude such
files.  This only affects afs and nfs; cifs uses the Windows Hidden
attribute to prevent the file from being seen.

Signed-off-by: David Howells <dhowells at redhat.com>
Link: https://lore.kernel.org/r/20241213135013.2964079-2-dhowells at redhat.com
cc: Masahiro Yamada <masahiroy at kernel.org>
cc: Marc Dionne <marc.dionne at auristor.com>
cc: linux-afs at lists.infradead.org
cc: linux-nfs at vger.kernel.org
cc: linux-kernel at vger.kernel.org
Signed-off-by: Christian Brauner <brauner at kernel.org>
Signed-off-by: Sasha Levin <sashal at kernel.org>
---
 kernel/gen_kheaders.sh | 1 +
 1 file changed, 1 insertion(+)

diff --git a/kernel/gen_kheaders.sh b/kernel/gen_kheaders.sh
index 383fd43ac612..7e1340da5aca 100755
--- a/kernel/gen_kheaders.sh
+++ b/kernel/gen_kheaders.sh
@@ -89,6 +89,7 @@ find $cpio_dir -type f -print0 |
 
 # Create archive and try to normalize metadata for reproducibility.
 tar "${KBUILD_BUILD_TIMESTAMP:+--mtime=$KBUILD_BUILD_TIMESTAMP}" \
+    --exclude=".__afs*" --exclude=".nfs*" \
     --owner=0 --group=0 --sort=name --numeric-owner --mode=u=rw,go=r,a+X \
     -I $XZ -cf $tarfile -C $cpio_dir/ . > /dev/null
 
-- 
2.39.5



From sashal at kernel.org  Mon Jan 13 10:35:05 2025
From: sashal at kernel.org (Sasha Levin)
Date: Mon, 13 Jan 2025 13:35:05 -0500
Subject: [PATCH AUTOSEL 6.6 04/10] kheaders: Ignore silly-rename files
In-Reply-To: <20250113183511.1783990-1-sashal@kernel.org>
References: <20250113183511.1783990-1-sashal@kernel.org>
Message-ID: <20250113183511.1783990-4-sashal@kernel.org>

From: David Howells <dhowells at redhat.com>

[ Upstream commit 973b710b8821c3401ad7a25360c89e94b26884ac ]

Tell tar to ignore silly-rename files (".__afs*" and ".nfs*") when building
the header archive.  These occur when a file that is open is unlinked
locally, but hasn't yet been closed.  Such files are visible to the user
via the getdents() syscall and so programs may want to do things with them.

During the kernel build, such files may be made during the processing of
header files and the cleanup may get deferred by fput() which may result in
tar seeing these files when it reads the directory, but they may have
disappeared by the time it tries to open them, causing tar to fail with an
error.  Further, we don't want to include them in the tarball if they still
exist.

With CONFIG_HEADERS_INSTALL=y, something like the following may be seen:

   find: './kernel/.tmp_cpio_dir/include/dt-bindings/reset/.__afs2080': No such file or directory
   tar: ./include/linux/greybus/.__afs3C95: File removed before we read it

The find warning doesn't seem to cause a problem.

Fix this by telling tar when called from in gen_kheaders.sh to exclude such
files.  This only affects afs and nfs; cifs uses the Windows Hidden
attribute to prevent the file from being seen.

Signed-off-by: David Howells <dhowells at redhat.com>
Link: https://lore.kernel.org/r/20241213135013.2964079-2-dhowells at redhat.com
cc: Masahiro Yamada <masahiroy at kernel.org>
cc: Marc Dionne <marc.dionne at auristor.com>
cc: linux-afs at lists.infradead.org
cc: linux-nfs at vger.kernel.org
cc: linux-kernel at vger.kernel.org
Signed-off-by: Christian Brauner <brauner at kernel.org>
Signed-off-by: Sasha Levin <sashal at kernel.org>
---
 kernel/gen_kheaders.sh | 1 +
 1 file changed, 1 insertion(+)

diff --git a/kernel/gen_kheaders.sh b/kernel/gen_kheaders.sh
index 383fd43ac612..7e1340da5aca 100755
--- a/kernel/gen_kheaders.sh
+++ b/kernel/gen_kheaders.sh
@@ -89,6 +89,7 @@ find $cpio_dir -type f -print0 |
 
 # Create archive and try to normalize metadata for reproducibility.
 tar "${KBUILD_BUILD_TIMESTAMP:+--mtime=$KBUILD_BUILD_TIMESTAMP}" \
+    --exclude=".__afs*" --exclude=".nfs*" \
     --owner=0 --group=0 --sort=name --numeric-owner --mode=u=rw,go=r,a+X \
     -I $XZ -cf $tarfile -C $cpio_dir/ . > /dev/null
 
-- 
2.39.5



From sashal at kernel.org  Mon Jan 13 10:35:30 2025
From: sashal at kernel.org (Sasha Levin)
Date: Mon, 13 Jan 2025 13:35:30 -0500
Subject: [PATCH AUTOSEL 6.1 04/10] kheaders: Ignore silly-rename files
In-Reply-To: <20250113183537.1784136-1-sashal@kernel.org>
References: <20250113183537.1784136-1-sashal@kernel.org>
Message-ID: <20250113183537.1784136-4-sashal@kernel.org>

From: David Howells <dhowells at redhat.com>

[ Upstream commit 973b710b8821c3401ad7a25360c89e94b26884ac ]

Tell tar to ignore silly-rename files (".__afs*" and ".nfs*") when building
the header archive.  These occur when a file that is open is unlinked
locally, but hasn't yet been closed.  Such files are visible to the user
via the getdents() syscall and so programs may want to do things with them.

During the kernel build, such files may be made during the processing of
header files and the cleanup may get deferred by fput() which may result in
tar seeing these files when it reads the directory, but they may have
disappeared by the time it tries to open them, causing tar to fail with an
error.  Further, we don't want to include them in the tarball if they still
exist.

With CONFIG_HEADERS_INSTALL=y, something like the following may be seen:

   find: './kernel/.tmp_cpio_dir/include/dt-bindings/reset/.__afs2080': No such file or directory
   tar: ./include/linux/greybus/.__afs3C95: File removed before we read it

The find warning doesn't seem to cause a problem.

Fix this by telling tar when called from in gen_kheaders.sh to exclude such
files.  This only affects afs and nfs; cifs uses the Windows Hidden
attribute to prevent the file from being seen.

Signed-off-by: David Howells <dhowells at redhat.com>
Link: https://lore.kernel.org/r/20241213135013.2964079-2-dhowells at redhat.com
cc: Masahiro Yamada <masahiroy at kernel.org>
cc: Marc Dionne <marc.dionne at auristor.com>
cc: linux-afs at lists.infradead.org
cc: linux-nfs at vger.kernel.org
cc: linux-kernel at vger.kernel.org
Signed-off-by: Christian Brauner <brauner at kernel.org>
Signed-off-by: Sasha Levin <sashal at kernel.org>
---
 kernel/gen_kheaders.sh | 1 +
 1 file changed, 1 insertion(+)

diff --git a/kernel/gen_kheaders.sh b/kernel/gen_kheaders.sh
index 12bcd08fe79d..5d506c6e8c0e 100755
--- a/kernel/gen_kheaders.sh
+++ b/kernel/gen_kheaders.sh
@@ -82,6 +82,7 @@ find $cpio_dir -type f -print0 |
 
 # Create archive and try to normalize metadata for reproducibility.
 tar "${KBUILD_BUILD_TIMESTAMP:+--mtime=$KBUILD_BUILD_TIMESTAMP}" \
+    --exclude=".__afs*" --exclude=".nfs*" \
     --owner=0 --group=0 --sort=name --numeric-owner --mode=u=rw,go=r,a+X \
     -I $XZ -cf $tarfile -C $cpio_dir/ . > /dev/null
 
-- 
2.39.5



From sashal at kernel.org  Mon Jan 13 10:35:58 2025
From: sashal at kernel.org (Sasha Levin)
Date: Mon, 13 Jan 2025 13:35:58 -0500
Subject: [PATCH AUTOSEL 5.15 4/6] kheaders: Ignore silly-rename files
In-Reply-To: <20250113183601.1784402-1-sashal@kernel.org>
References: <20250113183601.1784402-1-sashal@kernel.org>
Message-ID: <20250113183601.1784402-4-sashal@kernel.org>

From: David Howells <dhowells at redhat.com>

[ Upstream commit 973b710b8821c3401ad7a25360c89e94b26884ac ]

Tell tar to ignore silly-rename files (".__afs*" and ".nfs*") when building
the header archive.  These occur when a file that is open is unlinked
locally, but hasn't yet been closed.  Such files are visible to the user
via the getdents() syscall and so programs may want to do things with them.

During the kernel build, such files may be made during the processing of
header files and the cleanup may get deferred by fput() which may result in
tar seeing these files when it reads the directory, but they may have
disappeared by the time it tries to open them, causing tar to fail with an
error.  Further, we don't want to include them in the tarball if they still
exist.

With CONFIG_HEADERS_INSTALL=y, something like the following may be seen:

   find: './kernel/.tmp_cpio_dir/include/dt-bindings/reset/.__afs2080': No such file or directory
   tar: ./include/linux/greybus/.__afs3C95: File removed before we read it

The find warning doesn't seem to cause a problem.

Fix this by telling tar when called from in gen_kheaders.sh to exclude such
files.  This only affects afs and nfs; cifs uses the Windows Hidden
attribute to prevent the file from being seen.

Signed-off-by: David Howells <dhowells at redhat.com>
Link: https://lore.kernel.org/r/20241213135013.2964079-2-dhowells at redhat.com
cc: Masahiro Yamada <masahiroy at kernel.org>
cc: Marc Dionne <marc.dionne at auristor.com>
cc: linux-afs at lists.infradead.org
cc: linux-nfs at vger.kernel.org
cc: linux-kernel at vger.kernel.org
Signed-off-by: Christian Brauner <brauner at kernel.org>
Signed-off-by: Sasha Levin <sashal at kernel.org>
---
 kernel/gen_kheaders.sh | 1 +
 1 file changed, 1 insertion(+)

diff --git a/kernel/gen_kheaders.sh b/kernel/gen_kheaders.sh
index c618e37ccea9..1b2b61ca8065 100755
--- a/kernel/gen_kheaders.sh
+++ b/kernel/gen_kheaders.sh
@@ -82,6 +82,7 @@ find $cpio_dir -type f -print0 |
 
 # Create archive and try to normalize metadata for reproducibility.
 tar "${KBUILD_BUILD_TIMESTAMP:+--mtime=$KBUILD_BUILD_TIMESTAMP}" \
+    --exclude=".__afs*" --exclude=".nfs*" \
     --owner=0 --group=0 --sort=name --numeric-owner --mode=u=rw,go=r,a+X \
     -I $XZ -cf $tarfile -C $cpio_dir/ . > /dev/null
 
-- 
2.39.5



From sashal at kernel.org  Mon Jan 13 10:36:18 2025
From: sashal at kernel.org (Sasha Levin)
Date: Mon, 13 Jan 2025 13:36:18 -0500
Subject: [PATCH AUTOSEL 5.10 4/5] kheaders: Ignore silly-rename files
In-Reply-To: <20250113183619.1784510-1-sashal@kernel.org>
References: <20250113183619.1784510-1-sashal@kernel.org>
Message-ID: <20250113183619.1784510-4-sashal@kernel.org>

From: David Howells <dhowells at redhat.com>

[ Upstream commit 973b710b8821c3401ad7a25360c89e94b26884ac ]

Tell tar to ignore silly-rename files (".__afs*" and ".nfs*") when building
the header archive.  These occur when a file that is open is unlinked
locally, but hasn't yet been closed.  Such files are visible to the user
via the getdents() syscall and so programs may want to do things with them.

During the kernel build, such files may be made during the processing of
header files and the cleanup may get deferred by fput() which may result in
tar seeing these files when it reads the directory, but they may have
disappeared by the time it tries to open them, causing tar to fail with an
error.  Further, we don't want to include them in the tarball if they still
exist.

With CONFIG_HEADERS_INSTALL=y, something like the following may be seen:

   find: './kernel/.tmp_cpio_dir/include/dt-bindings/reset/.__afs2080': No such file or directory
   tar: ./include/linux/greybus/.__afs3C95: File removed before we read it

The find warning doesn't seem to cause a problem.

Fix this by telling tar when called from in gen_kheaders.sh to exclude such
files.  This only affects afs and nfs; cifs uses the Windows Hidden
attribute to prevent the file from being seen.

Signed-off-by: David Howells <dhowells at redhat.com>
Link: https://lore.kernel.org/r/20241213135013.2964079-2-dhowells at redhat.com
cc: Masahiro Yamada <masahiroy at kernel.org>
cc: Marc Dionne <marc.dionne at auristor.com>
cc: linux-afs at lists.infradead.org
cc: linux-nfs at vger.kernel.org
cc: linux-kernel at vger.kernel.org
Signed-off-by: Christian Brauner <brauner at kernel.org>
Signed-off-by: Sasha Levin <sashal at kernel.org>
---
 kernel/gen_kheaders.sh | 1 +
 1 file changed, 1 insertion(+)

diff --git a/kernel/gen_kheaders.sh b/kernel/gen_kheaders.sh
index 206ab3d41ee7..7fc44d8da205 100755
--- a/kernel/gen_kheaders.sh
+++ b/kernel/gen_kheaders.sh
@@ -84,6 +84,7 @@ find $cpio_dir -type f -print0 |
 
 # Create archive and try to normalize metadata for reproducibility.
 tar "${KBUILD_BUILD_TIMESTAMP:+--mtime=$KBUILD_BUILD_TIMESTAMP}" \
+    --exclude=".__afs*" --exclude=".nfs*" \
     --owner=0 --group=0 --sort=name --numeric-owner --mode=u=rw,go=r,a+X \
     -I $XZ -cf $tarfile -C $cpio_dir/ . > /dev/null
 
-- 
2.39.5



From sashal at kernel.org  Mon Jan 13 10:36:32 2025
From: sashal at kernel.org (Sasha Levin)
Date: Mon, 13 Jan 2025 13:36:32 -0500
Subject: [PATCH AUTOSEL 5.4 3/4] kheaders: Ignore silly-rename files
In-Reply-To: <20250113183633.1784590-1-sashal@kernel.org>
References: <20250113183633.1784590-1-sashal@kernel.org>
Message-ID: <20250113183633.1784590-3-sashal@kernel.org>

From: David Howells <dhowells at redhat.com>

[ Upstream commit 973b710b8821c3401ad7a25360c89e94b26884ac ]

Tell tar to ignore silly-rename files (".__afs*" and ".nfs*") when building
the header archive.  These occur when a file that is open is unlinked
locally, but hasn't yet been closed.  Such files are visible to the user
via the getdents() syscall and so programs may want to do things with them.

During the kernel build, such files may be made during the processing of
header files and the cleanup may get deferred by fput() which may result in
tar seeing these files when it reads the directory, but they may have
disappeared by the time it tries to open them, causing tar to fail with an
error.  Further, we don't want to include them in the tarball if they still
exist.

With CONFIG_HEADERS_INSTALL=y, something like the following may be seen:

   find: './kernel/.tmp_cpio_dir/include/dt-bindings/reset/.__afs2080': No such file or directory
   tar: ./include/linux/greybus/.__afs3C95: File removed before we read it

The find warning doesn't seem to cause a problem.

Fix this by telling tar when called from in gen_kheaders.sh to exclude such
files.  This only affects afs and nfs; cifs uses the Windows Hidden
attribute to prevent the file from being seen.

Signed-off-by: David Howells <dhowells at redhat.com>
Link: https://lore.kernel.org/r/20241213135013.2964079-2-dhowells at redhat.com
cc: Masahiro Yamada <masahiroy at kernel.org>
cc: Marc Dionne <marc.dionne at auristor.com>
cc: linux-afs at lists.infradead.org
cc: linux-nfs at vger.kernel.org
cc: linux-kernel at vger.kernel.org
Signed-off-by: Christian Brauner <brauner at kernel.org>
Signed-off-by: Sasha Levin <sashal at kernel.org>
---
 kernel/gen_kheaders.sh | 1 +
 1 file changed, 1 insertion(+)

diff --git a/kernel/gen_kheaders.sh b/kernel/gen_kheaders.sh
index 206ab3d41ee7..7fc44d8da205 100755
--- a/kernel/gen_kheaders.sh
+++ b/kernel/gen_kheaders.sh
@@ -84,6 +84,7 @@ find $cpio_dir -type f -print0 |
 
 # Create archive and try to normalize metadata for reproducibility.
 tar "${KBUILD_BUILD_TIMESTAMP:+--mtime=$KBUILD_BUILD_TIMESTAMP}" \
+    --exclude=".__afs*" --exclude=".nfs*" \
     --owner=0 --group=0 --sort=name --numeric-owner --mode=u=rw,go=r,a+X \
     -I $XZ -cf $tarfile -C $cpio_dir/ . > /dev/null
 
-- 
2.39.5



From gregkh at linuxfoundation.org  Wed Jan 15 02:36:51 2025
From: gregkh at linuxfoundation.org (Greg Kroah-Hartman)
Date: Wed, 15 Jan 2025 11:36:51 +0100
Subject: [PATCH 6.1 33/92] afs: Fix the maximum cell name length
In-Reply-To: <20250115103547.522503305@linuxfoundation.org>
References: <20250115103547.522503305@linuxfoundation.org>
Message-ID: <20250115103548.854521134@linuxfoundation.org>

6.1-stable review patch.  If anyone has any objections, please let me know.

------------------

From: David Howells <dhowells at redhat.com>

[ Upstream commit 8fd56ad6e7c90ac2bddb0741c6b248c8c5d56ac8 ]

The kafs filesystem limits the maximum length of a cell to 256 bytes, but a
problem occurs if someone actually does that: kafs tries to create a
directory under /proc/net/afs/ with the name of the cell, but that fails
with a warning:

        WARNING: CPU: 0 PID: 9 at fs/proc/generic.c:405

because procfs limits the maximum filename length to 255.

However, the DNS limits the maximum lookup length and, by extension, the
maximum cell name, to 255 less two (length count and trailing NUL).

Fix this by limiting the maximum acceptable cellname length to 253.  This
also allows us to be sure we can create the "/afs/.<cell>/" mountpoint too.

Further, split the YFS VL record cell name maximum to be the 256 allowed by
the protocol and ignore the record retrieved by YFSVL.GetCellName if it
exceeds 253.

Fixes: c3e9f888263b ("afs: Implement client support for the YFSVL.GetCellName RPC op")
Reported-by: syzbot+7848fee1f1e5c53f912b at syzkaller.appspotmail.com
Closes: https://lore.kernel.org/r/6776d25d.050a0220.3a8527.0048.GAE at google.com/
Signed-off-by: David Howells <dhowells at redhat.com>
Link: https://lore.kernel.org/r/376236.1736180460 at warthog.procyon.org.uk
Tested-by: syzbot+7848fee1f1e5c53f912b at syzkaller.appspotmail.com
cc: Marc Dionne <marc.dionne at auristor.com>
cc: linux-afs at lists.infradead.org
Signed-off-by: Christian Brauner <brauner at kernel.org>
Signed-off-by: Sasha Levin <sashal at kernel.org>
---
 fs/afs/afs.h      | 2 +-
 fs/afs/afs_vl.h   | 1 +
 fs/afs/vl_alias.c | 8 ++++++--
 fs/afs/vlclient.c | 2 +-
 4 files changed, 9 insertions(+), 4 deletions(-)

diff --git a/fs/afs/afs.h b/fs/afs/afs.h
index 432cb4b23961..3ea5f3e3c922 100644
--- a/fs/afs/afs.h
+++ b/fs/afs/afs.h
@@ -10,7 +10,7 @@
 
 #include <linux/in.h>
 
-#define AFS_MAXCELLNAME		256  	/* Maximum length of a cell name */
+#define AFS_MAXCELLNAME		253  	/* Maximum length of a cell name (DNS limited) */
 #define AFS_MAXVOLNAME		64  	/* Maximum length of a volume name */
 #define AFS_MAXNSERVERS		8   	/* Maximum servers in a basic volume record */
 #define AFS_NMAXNSERVERS	13  	/* Maximum servers in a N/U-class volume record */
diff --git a/fs/afs/afs_vl.h b/fs/afs/afs_vl.h
index 9c65ffb8a523..8da0899fbc08 100644
--- a/fs/afs/afs_vl.h
+++ b/fs/afs/afs_vl.h
@@ -13,6 +13,7 @@
 #define AFS_VL_PORT		7003	/* volume location service port */
 #define VL_SERVICE		52	/* RxRPC service ID for the Volume Location service */
 #define YFS_VL_SERVICE		2503	/* Service ID for AuriStor upgraded VL service */
+#define YFS_VL_MAXCELLNAME	256  	/* Maximum length of a cell name in YFS protocol */
 
 enum AFSVL_Operations {
 	VLGETENTRYBYID		= 503,	/* AFS Get VLDB entry by ID */
diff --git a/fs/afs/vl_alias.c b/fs/afs/vl_alias.c
index f04a80e4f5c3..83cf1bfbe343 100644
--- a/fs/afs/vl_alias.c
+++ b/fs/afs/vl_alias.c
@@ -302,6 +302,7 @@ static char *afs_vl_get_cell_name(struct afs_cell *cell, struct key *key)
 static int yfs_check_canonical_cell_name(struct afs_cell *cell, struct key *key)
 {
 	struct afs_cell *master;
+	size_t name_len;
 	char *cell_name;
 
 	cell_name = afs_vl_get_cell_name(cell, key);
@@ -313,8 +314,11 @@ static int yfs_check_canonical_cell_name(struct afs_cell *cell, struct key *key)
 		return 0;
 	}
 
-	master = afs_lookup_cell(cell->net, cell_name, strlen(cell_name),
-				 NULL, false);
+	name_len = strlen(cell_name);
+	if (!name_len || name_len > AFS_MAXCELLNAME)
+		master = ERR_PTR(-EOPNOTSUPP);
+	else
+		master = afs_lookup_cell(cell->net, cell_name, name_len, NULL, false);
 	kfree(cell_name);
 	if (IS_ERR(master))
 		return PTR_ERR(master);
diff --git a/fs/afs/vlclient.c b/fs/afs/vlclient.c
index 00fca3c66ba6..16653f2ffe4f 100644
--- a/fs/afs/vlclient.c
+++ b/fs/afs/vlclient.c
@@ -671,7 +671,7 @@ static int afs_deliver_yfsvl_get_cell_name(struct afs_call *call)
 			return ret;
 
 		namesz = ntohl(call->tmp);
-		if (namesz > AFS_MAXCELLNAME)
+		if (namesz > YFS_VL_MAXCELLNAME)
 			return afs_protocol_error(call, afs_eproto_cellname_len);
 		paddedsz = (namesz + 3) & ~3;
 		call->count = namesz;
-- 
2.39.5





From gregkh at linuxfoundation.org  Wed Jan 15 02:36:07 2025
From: gregkh at linuxfoundation.org (Greg Kroah-Hartman)
Date: Wed, 15 Jan 2025 11:36:07 +0100
Subject: [PATCH 6.12 071/189] afs: Fix the maximum cell name length
In-Reply-To: <20250115103606.357764746@linuxfoundation.org>
References: <20250115103606.357764746@linuxfoundation.org>
Message-ID: <20250115103609.194166682@linuxfoundation.org>

6.12-stable review patch.  If anyone has any objections, please let me know.

------------------

From: David Howells <dhowells at redhat.com>

[ Upstream commit 8fd56ad6e7c90ac2bddb0741c6b248c8c5d56ac8 ]

The kafs filesystem limits the maximum length of a cell to 256 bytes, but a
problem occurs if someone actually does that: kafs tries to create a
directory under /proc/net/afs/ with the name of the cell, but that fails
with a warning:

        WARNING: CPU: 0 PID: 9 at fs/proc/generic.c:405

because procfs limits the maximum filename length to 255.

However, the DNS limits the maximum lookup length and, by extension, the
maximum cell name, to 255 less two (length count and trailing NUL).

Fix this by limiting the maximum acceptable cellname length to 253.  This
also allows us to be sure we can create the "/afs/.<cell>/" mountpoint too.

Further, split the YFS VL record cell name maximum to be the 256 allowed by
the protocol and ignore the record retrieved by YFSVL.GetCellName if it
exceeds 253.

Fixes: c3e9f888263b ("afs: Implement client support for the YFSVL.GetCellName RPC op")
Reported-by: syzbot+7848fee1f1e5c53f912b at syzkaller.appspotmail.com
Closes: https://lore.kernel.org/r/6776d25d.050a0220.3a8527.0048.GAE at google.com/
Signed-off-by: David Howells <dhowells at redhat.com>
Link: https://lore.kernel.org/r/376236.1736180460 at warthog.procyon.org.uk
Tested-by: syzbot+7848fee1f1e5c53f912b at syzkaller.appspotmail.com
cc: Marc Dionne <marc.dionne at auristor.com>
cc: linux-afs at lists.infradead.org
Signed-off-by: Christian Brauner <brauner at kernel.org>
Signed-off-by: Sasha Levin <sashal at kernel.org>
---
 fs/afs/afs.h      | 2 +-
 fs/afs/afs_vl.h   | 1 +
 fs/afs/vl_alias.c | 8 ++++++--
 fs/afs/vlclient.c | 2 +-
 4 files changed, 9 insertions(+), 4 deletions(-)

diff --git a/fs/afs/afs.h b/fs/afs/afs.h
index b488072aee87..ec3db00bd081 100644
--- a/fs/afs/afs.h
+++ b/fs/afs/afs.h
@@ -10,7 +10,7 @@
 
 #include <linux/in.h>
 
-#define AFS_MAXCELLNAME		256  	/* Maximum length of a cell name */
+#define AFS_MAXCELLNAME		253  	/* Maximum length of a cell name (DNS limited) */
 #define AFS_MAXVOLNAME		64  	/* Maximum length of a volume name */
 #define AFS_MAXNSERVERS		8   	/* Maximum servers in a basic volume record */
 #define AFS_NMAXNSERVERS	13  	/* Maximum servers in a N/U-class volume record */
diff --git a/fs/afs/afs_vl.h b/fs/afs/afs_vl.h
index a06296c8827d..b835e25a2c02 100644
--- a/fs/afs/afs_vl.h
+++ b/fs/afs/afs_vl.h
@@ -13,6 +13,7 @@
 #define AFS_VL_PORT		7003	/* volume location service port */
 #define VL_SERVICE		52	/* RxRPC service ID for the Volume Location service */
 #define YFS_VL_SERVICE		2503	/* Service ID for AuriStor upgraded VL service */
+#define YFS_VL_MAXCELLNAME	256  	/* Maximum length of a cell name in YFS protocol */
 
 enum AFSVL_Operations {
 	VLGETENTRYBYID		= 503,	/* AFS Get VLDB entry by ID */
diff --git a/fs/afs/vl_alias.c b/fs/afs/vl_alias.c
index 9f36e14f1c2d..f9e76b604f31 100644
--- a/fs/afs/vl_alias.c
+++ b/fs/afs/vl_alias.c
@@ -253,6 +253,7 @@ static char *afs_vl_get_cell_name(struct afs_cell *cell, struct key *key)
 static int yfs_check_canonical_cell_name(struct afs_cell *cell, struct key *key)
 {
 	struct afs_cell *master;
+	size_t name_len;
 	char *cell_name;
 
 	cell_name = afs_vl_get_cell_name(cell, key);
@@ -264,8 +265,11 @@ static int yfs_check_canonical_cell_name(struct afs_cell *cell, struct key *key)
 		return 0;
 	}
 
-	master = afs_lookup_cell(cell->net, cell_name, strlen(cell_name),
-				 NULL, false);
+	name_len = strlen(cell_name);
+	if (!name_len || name_len > AFS_MAXCELLNAME)
+		master = ERR_PTR(-EOPNOTSUPP);
+	else
+		master = afs_lookup_cell(cell->net, cell_name, name_len, NULL, false);
 	kfree(cell_name);
 	if (IS_ERR(master))
 		return PTR_ERR(master);
diff --git a/fs/afs/vlclient.c b/fs/afs/vlclient.c
index cac75f89b64a..55dd0fc5aad7 100644
--- a/fs/afs/vlclient.c
+++ b/fs/afs/vlclient.c
@@ -697,7 +697,7 @@ static int afs_deliver_yfsvl_get_cell_name(struct afs_call *call)
 			return ret;
 
 		namesz = ntohl(call->tmp);
-		if (namesz > AFS_MAXCELLNAME)
+		if (namesz > YFS_VL_MAXCELLNAME)
 			return afs_protocol_error(call, afs_eproto_cellname_len);
 		paddedsz = (namesz + 3) & ~3;
 		call->count = namesz;
-- 
2.39.5





From brauner at kernel.org  Wed Jan 15 02:47:36 2025
From: brauner at kernel.org (Christian Brauner)
Date: Wed, 15 Jan 2025 11:47:36 +0100
Subject: [PATCH] afs: Fix the fallback handling for the YFS.RemoveFile2 RPC call
In-Reply-To: <109541.1736865963@warthog.procyon.org.uk>
References: <109541.1736865963@warthog.procyon.org.uk>
Message-ID: <20250115-gepfiffen-wenig-799588d55bb5@brauner>

On Tue, 14 Jan 2025 14:46:03 +0000, David Howells wrote:
> Fix a pair of bugs in the fallback handling for the YFS.RemoveFile2 RPC
> call:
> 
>  (1) Fix the abort code check to also look for RXGEN_OPCODE.  The lack of
>      this masks the second bug.
> 
>  (2) call->server is now not used for ordinary filesystem RPC calls that
>      have an operation descriptor.  Fix to use call->op->server instead.
> 
> [...]

Applied to the vfs-6.14.afs branch of the vfs/vfs.git tree.
Patches in the vfs-6.14.afs branch should appear in linux-next soon.

Please report any outstanding bugs that were missed during review in a
new review to the original patch series allowing us to drop it.

It's encouraged to provide Acked-bys and Reviewed-bys even though the
patch has now been applied. If possible patch trailers will be updated.

Note that commit hashes shown below are subject to change due to rebase,
trailer updates or similar. If in doubt, please check the listed branch.

tree:   https://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs.git
branch: vfs-6.14.afs

[1/1] afs: Fix the fallback handling for the YFS.RemoveFile2 RPC call
      https://git.kernel.org/vfs/vfs/c/e30458d690f3


From gregkh at linuxfoundation.org  Wed Jan 15 02:37:08 2025
From: gregkh at linuxfoundation.org (Greg Kroah-Hartman)
Date: Wed, 15 Jan 2025 11:37:08 +0100
Subject: [PATCH 6.6 053/129] afs: Fix the maximum cell name length
In-Reply-To: <20250115103554.357917208@linuxfoundation.org>
References: <20250115103554.357917208@linuxfoundation.org>
Message-ID: <20250115103556.485041337@linuxfoundation.org>

6.6-stable review patch.  If anyone has any objections, please let me know.

------------------

From: David Howells <dhowells at redhat.com>

[ Upstream commit 8fd56ad6e7c90ac2bddb0741c6b248c8c5d56ac8 ]

The kafs filesystem limits the maximum length of a cell to 256 bytes, but a
problem occurs if someone actually does that: kafs tries to create a
directory under /proc/net/afs/ with the name of the cell, but that fails
with a warning:

        WARNING: CPU: 0 PID: 9 at fs/proc/generic.c:405

because procfs limits the maximum filename length to 255.

However, the DNS limits the maximum lookup length and, by extension, the
maximum cell name, to 255 less two (length count and trailing NUL).

Fix this by limiting the maximum acceptable cellname length to 253.  This
also allows us to be sure we can create the "/afs/.<cell>/" mountpoint too.

Further, split the YFS VL record cell name maximum to be the 256 allowed by
the protocol and ignore the record retrieved by YFSVL.GetCellName if it
exceeds 253.

Fixes: c3e9f888263b ("afs: Implement client support for the YFSVL.GetCellName RPC op")
Reported-by: syzbot+7848fee1f1e5c53f912b at syzkaller.appspotmail.com
Closes: https://lore.kernel.org/r/6776d25d.050a0220.3a8527.0048.GAE at google.com/
Signed-off-by: David Howells <dhowells at redhat.com>
Link: https://lore.kernel.org/r/376236.1736180460 at warthog.procyon.org.uk
Tested-by: syzbot+7848fee1f1e5c53f912b at syzkaller.appspotmail.com
cc: Marc Dionne <marc.dionne at auristor.com>
cc: linux-afs at lists.infradead.org
Signed-off-by: Christian Brauner <brauner at kernel.org>
Signed-off-by: Sasha Levin <sashal at kernel.org>
---
 fs/afs/afs.h      | 2 +-
 fs/afs/afs_vl.h   | 1 +
 fs/afs/vl_alias.c | 8 ++++++--
 fs/afs/vlclient.c | 2 +-
 4 files changed, 9 insertions(+), 4 deletions(-)

diff --git a/fs/afs/afs.h b/fs/afs/afs.h
index 81815724db6c..25c17100798b 100644
--- a/fs/afs/afs.h
+++ b/fs/afs/afs.h
@@ -10,7 +10,7 @@
 
 #include <linux/in.h>
 
-#define AFS_MAXCELLNAME		256  	/* Maximum length of a cell name */
+#define AFS_MAXCELLNAME		253  	/* Maximum length of a cell name (DNS limited) */
 #define AFS_MAXVOLNAME		64  	/* Maximum length of a volume name */
 #define AFS_MAXNSERVERS		8   	/* Maximum servers in a basic volume record */
 #define AFS_NMAXNSERVERS	13  	/* Maximum servers in a N/U-class volume record */
diff --git a/fs/afs/afs_vl.h b/fs/afs/afs_vl.h
index 9c65ffb8a523..8da0899fbc08 100644
--- a/fs/afs/afs_vl.h
+++ b/fs/afs/afs_vl.h
@@ -13,6 +13,7 @@
 #define AFS_VL_PORT		7003	/* volume location service port */
 #define VL_SERVICE		52	/* RxRPC service ID for the Volume Location service */
 #define YFS_VL_SERVICE		2503	/* Service ID for AuriStor upgraded VL service */
+#define YFS_VL_MAXCELLNAME	256  	/* Maximum length of a cell name in YFS protocol */
 
 enum AFSVL_Operations {
 	VLGETENTRYBYID		= 503,	/* AFS Get VLDB entry by ID */
diff --git a/fs/afs/vl_alias.c b/fs/afs/vl_alias.c
index f04a80e4f5c3..83cf1bfbe343 100644
--- a/fs/afs/vl_alias.c
+++ b/fs/afs/vl_alias.c
@@ -302,6 +302,7 @@ static char *afs_vl_get_cell_name(struct afs_cell *cell, struct key *key)
 static int yfs_check_canonical_cell_name(struct afs_cell *cell, struct key *key)
 {
 	struct afs_cell *master;
+	size_t name_len;
 	char *cell_name;
 
 	cell_name = afs_vl_get_cell_name(cell, key);
@@ -313,8 +314,11 @@ static int yfs_check_canonical_cell_name(struct afs_cell *cell, struct key *key)
 		return 0;
 	}
 
-	master = afs_lookup_cell(cell->net, cell_name, strlen(cell_name),
-				 NULL, false);
+	name_len = strlen(cell_name);
+	if (!name_len || name_len > AFS_MAXCELLNAME)
+		master = ERR_PTR(-EOPNOTSUPP);
+	else
+		master = afs_lookup_cell(cell->net, cell_name, name_len, NULL, false);
 	kfree(cell_name);
 	if (IS_ERR(master))
 		return PTR_ERR(master);
diff --git a/fs/afs/vlclient.c b/fs/afs/vlclient.c
index 00fca3c66ba6..16653f2ffe4f 100644
--- a/fs/afs/vlclient.c
+++ b/fs/afs/vlclient.c
@@ -671,7 +671,7 @@ static int afs_deliver_yfsvl_get_cell_name(struct afs_call *call)
 			return ret;
 
 		namesz = ntohl(call->tmp);
-		if (namesz > AFS_MAXCELLNAME)
+		if (namesz > YFS_VL_MAXCELLNAME)
 			return afs_protocol_error(call, afs_eproto_cellname_len);
 		paddedsz = (namesz + 3) & ~3;
 		call->count = namesz;
-- 
2.39.5





From dhowells at redhat.com  Fri Jan 17 00:13:49 2025
From: dhowells at redhat.com (David Howells)
Date: Fri, 17 Jan 2025 08:13:49 +0000
Subject: [RFC PATCH 2/8] crypto/krb5: Provide Kerberos 5 crypto through AEAD API
In-Reply-To: <Z4Ds9NBiXUti-idl@gondor.apana.org.au>
References: <Z4Ds9NBiXUti-idl@gondor.apana.org.au> <20250110010313.1471063-1-dhowells@redhat.com> <20250110010313.1471063-3-dhowells@redhat.com>
Message-ID: <477969.1737101629@warthog.procyon.org.uk>

Herbert Xu <herbert at gondor.apana.org.au> wrote:

> rfc8009 is basically the same as authenc.

Actually, it's not quite the same :-/

rfc8009 chucks the IV from the encryption into the hash first, but authenc()
does not.  It may be possible to arrange the buffer so that the assoc data is
also the IV buffer.

David



From dhowells at redhat.com  Fri Jan 17 00:30:16 2025
From: dhowells at redhat.com (David Howells)
Date: Fri, 17 Jan 2025 08:30:16 +0000
Subject: [RFC PATCH 2/8] crypto/krb5: Provide Kerberos 5 crypto through AEAD API
In-Reply-To: <477969.1737101629@warthog.procyon.org.uk>
References: <477969.1737101629@warthog.procyon.org.uk> <Z4Ds9NBiXUti-idl@gondor.apana.org.au> <20250110010313.1471063-1-dhowells@redhat.com> <20250110010313.1471063-3-dhowells@redhat.com>
Message-ID: <493307.1737102616@warthog.procyon.org.uk>

David Howells <dhowells at redhat.com> wrote:

> > rfc8009 is basically the same as authenc.
> 
> Actually, it's not quite the same :-/
> 
> rfc8009 chucks the IV from the encryption into the hash first, but authenc()
> does not.  It may be possible to arrange the buffer so that the assoc data is
> also the IV buffer.

Actually actually, it's the starting IV, so I just need to chuck in a block of
zeroes.

David



From dhowells at redhat.com  Fri Jan 17 10:35:09 2025
From: dhowells at redhat.com (David Howells)
Date: Fri, 17 Jan 2025 18:35:09 +0000
Subject: [RFC PATCH 00/24] crypto: Add generic Kerberos library with AEAD template for hash-then-crypt
Message-ID: <20250117183538.881618-1-dhowells@redhat.com>

Hi Herbert, Chuck,

Here's yet another go at a generic Kerberos crypto library in the kernel so
that I can share code between rxrpc and sunrpc (and cifs?).  I derived some
of the parts from the sunrpc gss library and added more advanced AES and
Camellia crypto.

I added an addition AEAD template type, derived from authenc, that does
hash-then-crypt rather than crypt-then-hash as authenc does.  I called it
"krb5enc" for want of a better name.  Possibly I should call it "hashenc"
or something like that.

I went back to my previous more library-based approach, but for encrypt
mode, I replaced the separate skcipher and shash objects with a single
templated AEAD object - either krb5enc (AES+SHA1 and Camellia) or authenc
(AES+SHA2).

Apart from that, things are much as they were previously from the point of
view of someone using the API.  There's a new pair of functions that, given
an encoding type, a key and a usage value, will derive the necessary keys
and return an AEAD or hash handle.  These handles can then be passed to
operation functions that will do the actual work of performing an
encryption, a decryption, MIC generation or MIC verification.

Querying functions are also available to look up an encoding type table by
kerberos protocol number and to help manage message layout.

This library has its own self-testing framework that checks more things
than is possible with the testmgr, including subkey derivation.  It also
checks things about the output of encrypt + decrypt that testmgr doesn't.
That said, testmgr is also provisioned with some encryption and
checksumming tests for Camilla and AES2, though these have to be
provisioned with the intermediate subkeys rather than the transport key.

Note that, for purposes of illustration, I've included some rxrpc patches
that use this interface to implement the rxgk Rx security class.  The
branch also is based on net-next that carries some rxrpc patches that are a
prerequisite for this, but the crypto patches don't need it.

---
The patches can be found here also:

	http://git.kernel.org/cgit/linux/kernel/git/dhowells/linux-fs.git/log/?h=crypto-krb5

David

David Howells (24):
  crypto/krb5: Add API Documentation
  crypto/krb5: Add some constants out of sunrpc headers
  crypto: Add 'krb5enc' hash and cipher AEAD algorithm
  crypto/krb5: Test manager data
  crypto/krb5: Implement Kerberos crypto core
  crypto/krb5: Add an API to query the layout of the crypto section
  crypto/krb5: Add an API to alloc and prepare a crypto object
  crypto/krb5: Add an API to perform requests
  crypto/krb5: Provide infrastructure and key derivation
  crypto/krb5: Implement the Kerberos5 rfc3961 key derivation
  crypto/krb5: Provide RFC3961 setkey packaging functions
  crypto/krb5: Implement the Kerberos5 rfc3961 encrypt and decrypt
    functions
  crypto/krb5: Implement the Kerberos5 rfc3961 get_mic and verify_mic
  crypto/krb5: Implement the AES enctypes from rfc3962
  crypto/krb5: Implement the AES enctypes from rfc8009
  crypto/krb5: Implement the AES encrypt/decrypt from rfc8009
  crypto/krb5: Implement crypto self-testing
  crypto/krb5: Add the AES self-testing data from rfc8009
  crypto/krb5: Implement the Camellia enctypes from rfc6803
  rxrpc: Add the security index for yfs-rxgk
  rxrpc: Add YFS RxGK (GSSAPI) security class
  rxrpc: rxgk: Provide infrastructure and key derivation
  rxrpc: rxgk: Implement the yfs-rxgk security class (GSSAPI)
  rxrpc: rxgk: Implement connection rekeying

 Documentation/crypto/index.rst   |    1 +
 Documentation/crypto/krb5.rst    |  262 +++++++
 crypto/Kconfig                   |   13 +
 crypto/Makefile                  |    3 +
 crypto/krb5/Kconfig              |   26 +
 crypto/krb5/Makefile             |   18 +
 crypto/krb5/internal.h           |  257 +++++++
 crypto/krb5/krb5_api.c           |  452 ++++++++++++
 crypto/krb5/krb5_kdf.c           |  145 ++++
 crypto/krb5/rfc3961_simplified.c |  791 ++++++++++++++++++++
 crypto/krb5/rfc3962_aes.c        |  115 +++
 crypto/krb5/rfc6803_camellia.c   |  237 ++++++
 crypto/krb5/rfc8009_aes2.c       |  431 +++++++++++
 crypto/krb5/selftest.c           |  544 ++++++++++++++
 crypto/krb5/selftest_data.c      |  291 ++++++++
 crypto/krb5enc.c                 |  491 +++++++++++++
 crypto/testmgr.c                 |   16 +
 crypto/testmgr.h                 |  401 ++++++++++
 fs/afs/misc.c                    |   13 +
 include/crypto/krb5.h            |  167 +++++
 include/keys/rxrpc-type.h        |   17 +
 include/trace/events/rxrpc.h     |   36 +
 include/uapi/linux/rxrpc.h       |   17 +
 net/rxrpc/Kconfig                |   10 +
 net/rxrpc/Makefile               |    5 +-
 net/rxrpc/ar-internal.h          |   22 +
 net/rxrpc/conn_event.c           |    2 +-
 net/rxrpc/conn_object.c          |    1 +
 net/rxrpc/key.c                  |  183 +++++
 net/rxrpc/output.c               |    2 +-
 net/rxrpc/protocol.h             |   20 +
 net/rxrpc/rxgk.c                 | 1170 ++++++++++++++++++++++++++++++
 net/rxrpc/rxgk_app.c             |  284 ++++++++
 net/rxrpc/rxgk_common.h          |  140 ++++
 net/rxrpc/rxgk_kdf.c             |  287 ++++++++
 net/rxrpc/rxkad.c                |    6 +-
 net/rxrpc/security.c             |    3 +
 37 files changed, 6874 insertions(+), 5 deletions(-)
 create mode 100644 Documentation/crypto/krb5.rst
 create mode 100644 crypto/krb5/Kconfig
 create mode 100644 crypto/krb5/Makefile
 create mode 100644 crypto/krb5/internal.h
 create mode 100644 crypto/krb5/krb5_api.c
 create mode 100644 crypto/krb5/krb5_kdf.c
 create mode 100644 crypto/krb5/rfc3961_simplified.c
 create mode 100644 crypto/krb5/rfc3962_aes.c
 create mode 100644 crypto/krb5/rfc6803_camellia.c
 create mode 100644 crypto/krb5/rfc8009_aes2.c
 create mode 100644 crypto/krb5/selftest.c
 create mode 100644 crypto/krb5/selftest_data.c
 create mode 100644 crypto/krb5enc.c
 create mode 100644 include/crypto/krb5.h
 create mode 100644 net/rxrpc/rxgk.c
 create mode 100644 net/rxrpc/rxgk_app.c
 create mode 100644 net/rxrpc/rxgk_common.h
 create mode 100644 net/rxrpc/rxgk_kdf.c



From dhowells at redhat.com  Fri Jan 17 10:35:10 2025
From: dhowells at redhat.com (David Howells)
Date: Fri, 17 Jan 2025 18:35:10 +0000
Subject: [RFC PATCH 01/24] crypto/krb5: Add API Documentation
In-Reply-To: <20250117183538.881618-1-dhowells@redhat.com>
References: <20250117183538.881618-1-dhowells@redhat.com>
Message-ID: <20250117183538.881618-2-dhowells@redhat.com>

Add API documentation for the kerberos crypto library.

Signed-off-by: David Howells <dhowells at redhat.com>
cc: Herbert Xu <herbert at gondor.apana.org.au>
cc: "David S. Miller" <davem at davemloft.net>
cc: Chuck Lever <chuck.lever at oracle.com>
cc: Marc Dionne <marc.dionne at auristor.com>
cc: Eric Dumazet <edumazet at google.com>
cc: Jakub Kicinski <kuba at kernel.org>
cc: Paolo Abeni <pabeni at redhat.com>
cc: Simon Horman <horms at kernel.org>
cc: linux-afs at lists.infradead.org
cc: linux-nfs at vger.kernel.org
cc: linux-crypto at vger.kernel.org
cc: netdev at vger.kernel.org
---
 Documentation/crypto/index.rst |   1 +
 Documentation/crypto/krb5.rst  | 262 +++++++++++++++++++++++++++++++++
 2 files changed, 263 insertions(+)
 create mode 100644 Documentation/crypto/krb5.rst

diff --git a/Documentation/crypto/index.rst b/Documentation/crypto/index.rst
index 92eec78b5713..100b47d049c0 100644
--- a/Documentation/crypto/index.rst
+++ b/Documentation/crypto/index.rst
@@ -26,3 +26,4 @@ for cryptographic use cases, as well as programming examples.
    api-samples
    descore-readme
    device_drivers/index
+   krb5
diff --git a/Documentation/crypto/krb5.rst b/Documentation/crypto/krb5.rst
new file mode 100644
index 000000000000..beffa0133446
--- /dev/null
+++ b/Documentation/crypto/krb5.rst
@@ -0,0 +1,262 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===========================
+Kerberos V Cryptography API
+===========================
+
+.. Contents:
+
+  - Overview.
+    - Small Buffer.
+  - Encoding Type.
+  - Key Derivation.
+    - PRF+ Calculation.
+    - Kc, Ke And Ki Derivation.
+  - Crypto Functions.
+    - Preparation Functions.
+    - Encryption Mode.
+    - Checksum Mode.
+  - The krb5enc AEAD algorithm
+
+Overview
+========
+
+This API provides Kerberos 5-style cryptography for key derivation, encryption
+and checksumming for use in network filesystems and can be used to implement
+the low-level crypto that's needed for GSSAPI.
+
+The following crypto types are supported::
+
+	KRB5_ENCTYPE_AES128_CTS_HMAC_SHA1_96
+	KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96
+	KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128
+	KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192
+	KRB5_ENCTYPE_CAMELLIA128_CTS_CMAC
+	KRB5_ENCTYPE_CAMELLIA256_CTS_CMAC
+
+	KRB5_CKSUMTYPE_HMAC_SHA1_96_AES128
+	KRB5_CKSUMTYPE_HMAC_SHA1_96_AES256
+	KRB5_CKSUMTYPE_CMAC_CAMELLIA128
+	KRB5_CKSUMTYPE_CMAC_CAMELLIA256
+	KRB5_CKSUMTYPE_HMAC_SHA256_128_AES128
+	KRB5_CKSUMTYPE_HMAC_SHA384_192_AES256
+
+The API can be included by::
+
+	#include <crypto/krb5.h>
+
+Small Buffer
+------------
+
+To pass small pieces of data about, such as keys, a buffer structure is
+defined, giving a pointer to the data and the size of that data::
+
+	struct krb5_buffer {
+		unsigned int	len;
+		void		*data;
+	};
+
+Encoding Type
+=============
+
+The encoding type is defined by the following structure::
+
+	struct krb5_enctype {
+		int		etype;
+		int		ctype;
+		const char	*name;
+		u16		key_bytes;
+		u16		key_len;
+		u16		Kc_len;
+		u16		Ke_len;
+		u16		Ki_len;
+		u16		prf_len;
+		u16		block_len;
+		u16		conf_len;
+		u16		cksum_len;
+		...
+	};
+
+The fields of interest to the user of the API are as follows:
+
+  * ``etype`` and ``ctype`` indicate the protocol number for this encoding
+    type for encryption and checksumming respectively.  They hold
+    ``KRB5_ENCTYPE_*`` and ``KRB5_CKSUMTYPE_*`` constants.
+
+  * ``name`` is the formal name of the encoding.
+
+  * ``key_len`` and ``key_bytes`` are the input key length and the derived key
+    length.  (I think they only differ for DES, which isn't supported here).
+
+  * ``Kc_len``, ``Ke_len`` and ``Ki_len`` are the sizes of the derived Kc, Ke
+    and Ki keys.  Kc is used for in checksum mode; Ke and Ki are used in
+    encryption mode.
+
+  * ``prf_len`` is the size of the result from the PRF+ function calculation.
+
+  * ``block_len``, ``conf_len`` and ``cksum_len`` are the encryption block
+    length, confounder length and checksum length respectively.  All three are
+    used in encryption mode, but only the checksum length is used in checksum
+    mode.
+
+The encoding type is looked up by number using the following function::
+
+	const struct krb5_enctype *crypto_krb5_find_enctype(u32 enctype);
+
+Key Derivation
+==============
+
+Once the application has selected an encryption type, the keys that will be
+used to do the actual crypto can be derived from the transport key.
+
+PRF+ Calculation
+----------------
+
+To aid in key derivation, a function to calculate the Kerberos GSSAPI
+mechanism's PRF+ is provided::
+
+	int crypto_krb5_calc_PRFplus(const struct krb5_enctype *krb5,
+				     const struct krb5_buffer *K,
+				     unsigned int L,
+				     const struct krb5_buffer *S,
+				     struct krb5_buffer *result,
+				     gfp_t gfp);
+
+This can be used to derive the transport key from a source key plus additional
+data to limit its use.
+
+Crypto Functions
+================
+
+Once the keys have been derived, crypto can be performed on the data.  The
+caller must leave gaps in the buffer for the storage of the confounder (if
+needed) and the checksum when preparing a message for transmission.  An enum
+and a pair of functions are provided to aid in this::
+
+	enum krb5_crypto_mode {
+		KRB5_CHECKSUM_MODE,
+		KRB5_ENCRYPT_MODE,
+	};
+
+	size_t crypto_krb5_how_much_buffer(const struct krb5_enctype *krb5,
+					   enum krb5_crypto_mode mode,
+					   size_t data_size, size_t *_offset);
+
+	size_t crypto_krb5_how_much_data(const struct krb5_enctype *krb5,
+					 enum krb5_crypto_mode mode,
+					 size_t *_buffer_size, size_t *_offset);
+
+All these functions take the encoding type and an indication the mode of crypto
+(checksum-only or full encryption).
+
+The first function returns how big the buffer will need to be to house a given
+amount of data; the second function returns how much data will fit in a buffer
+of a particular size, and adjusts down the size of the required buffer
+accordingly.  In both cases, the offset of the data within the buffer is also
+returned.
+
+When a message has been received, the location and size of the data with the
+message can be determined by calling::
+
+	void crypto_krb5_where_is_the_data(const struct krb5_enctype *krb5,
+					   enum krb5_crypto_mode mode,
+					   size_t *_offset, size_t *_len);
+
+The caller provides the offset and length of the message to the function, which
+then alters those values to indicate the region containing the data (plus any
+padding).  It is up to the caller to determine how much padding there is.
+
+Preparation Functions
+---------------------
+
+Two functions are provided to allocated and prepare a crypto object for use by
+the action functions::
+
+	struct crypto_aead *
+	crypto_krb5_prepare_encryption(const struct krb5_enctype *krb5,
+				       const struct krb5_buffer *TK,
+				       u32 usage, gfp_t gfp);
+	struct crypto_shash *
+	crypto_krb5_prepare_checksum(const struct krb5_enctype *krb5,
+				     const struct krb5_buffer *TK,
+				     u32 usage, gfp_t gfp);
+
+Both of these functions take the encoding type, the transport key and the usage
+value used to derive the appropriate subkey(s).  They create an appropriate
+crypto object, an AEAD template for encryption and a synchronous hash for
+checksumming, set the key(s) on it and configure it.  The caller is expected to
+pass these handles to the action functions below.
+
+Encryption Mode
+---------------
+
+A pair of functions are provided to encrypt and decrypt a message::
+
+	ssize_t crypto_krb5_encrypt(const struct krb5_enctype *krb5,
+				    struct crypto_aead *aead,
+				    struct scatterlist *sg, unsigned int nr_sg,
+				    size_t sg_len,
+				    size_t data_offset, size_t data_len,
+				    bool preconfounded);
+	int crypto_krb5_decrypt(const struct krb5_enctype *krb5,
+				struct crypto_aead *aead,
+				struct scatterlist *sg, unsigned int nr_sg,
+				size_t *_offset, size_t *_len);
+
+In both cases, the input and output buffers are indicated by the same
+scatterlist.
+
+For the encryption function, the output buffer may be larger than is needed
+(the amount of output generated is returned) and the location and size of the
+data are indicated (which must match the encoding).  If no confounder is set,
+the function will insert one.
+
+For the decryption function, the offset and length of the message in buffer are
+supplied and these are shrunk to fit the data.  The decryption function will
+verify any checksums within the message and give an error if they don't match.
+
+Checksum Mode
+-------------
+
+A pair of function are provided to generate the checksum on a message and to
+verify that checksum::
+
+	ssize_t crypto_krb5_get_mic(const struct krb5_enctype *krb5,
+				    struct crypto_shash *shash,
+				    const struct krb5_buffer *metadata,
+				    struct scatterlist *sg, unsigned int nr_sg,
+				    size_t sg_len,
+				    size_t data_offset, size_t data_len);
+	int crypto_krb5_verify_mic(const struct krb5_enctype *krb5,
+				   struct crypto_shash *shash,
+				   const struct krb5_buffer *metadata,
+				   struct scatterlist *sg, unsigned int nr_sg,
+				   size_t *_offset, size_t *_len);
+
+In both cases, the input and output buffers are indicated by the same
+scatterlist.  Additional metadata can be passed in which will get added to the
+hash before the data.
+
+For the get_mic function, the output buffer may be larger than is needed (the
+amount of output generated is returned) and the location and size of the data
+are indicated (which must match the encoding).
+
+For the verification function, the offset and length of the message in buffer
+are supplied and these are shrunk to fit the data.  An error will be returned
+if the checksums don't match.
+
+The krb5enc AEAD algorithm
+==========================
+
+A template AEAD crypto algorithm, called "krb5enc", is provided that hashes the
+plaintext before encrypting it (the reverse of authenc).  The handle returned
+by ``crypto_krb5_prepare_encryption()`` may be one of these, but there's no
+requirement for the user of this API to interact with it directly.
+
+For reference, its key format begins with a BE32 of the format number.  Only
+format 1 is provided and that continues with a BE32 of the Ke key length
+followed by a BE32 of the Ki key length, followed by the bytes from the Ke key
+and then the Ki key.
+
+Using specifically ordered words means that the static test data doesn't
+require byteswapping.



From dhowells at redhat.com  Fri Jan 17 10:35:11 2025
From: dhowells at redhat.com (David Howells)
Date: Fri, 17 Jan 2025 18:35:11 +0000
Subject: [RFC PATCH 02/24] crypto/krb5: Add some constants out of sunrpc headers
In-Reply-To: <20250117183538.881618-1-dhowells@redhat.com>
References: <20250117183538.881618-1-dhowells@redhat.com>
Message-ID: <20250117183538.881618-3-dhowells@redhat.com>

Add some constants from the sunrpc headers.

Signed-off-by: David Howells <dhowells at redhat.com>
cc: Herbert Xu <herbert at gondor.apana.org.au>
cc: "David S. Miller" <davem at davemloft.net>
cc: Chuck Lever <chuck.lever at oracle.com>
cc: Marc Dionne <marc.dionne at auristor.com>
cc: Eric Dumazet <edumazet at google.com>
cc: Jakub Kicinski <kuba at kernel.org>
cc: Paolo Abeni <pabeni at redhat.com>
cc: Simon Horman <horms at kernel.org>
cc: linux-afs at lists.infradead.org
cc: linux-nfs at vger.kernel.org
cc: linux-crypto at vger.kernel.org
cc: netdev at vger.kernel.org
---
 include/crypto/krb5.h | 51 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 51 insertions(+)
 create mode 100644 include/crypto/krb5.h

diff --git a/include/crypto/krb5.h b/include/crypto/krb5.h
new file mode 100644
index 000000000000..44a6342471d7
--- /dev/null
+++ b/include/crypto/krb5.h
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* Kerberos 5 crypto
+ *
+ * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells at redhat.com)
+ */
+
+#ifndef _CRYPTO_KRB5_H
+#define _CRYPTO_KRB5_H
+
+/*
+ * Per Kerberos v5 protocol spec crypto types from the wire.  These get mapped
+ * to linux kernel crypto routines.
+ */
+#define KRB5_ENCTYPE_NULL			0x0000
+#define KRB5_ENCTYPE_DES_CBC_CRC		0x0001	/* DES cbc mode with CRC-32 */
+#define KRB5_ENCTYPE_DES_CBC_MD4		0x0002	/* DES cbc mode with RSA-MD4 */
+#define KRB5_ENCTYPE_DES_CBC_MD5		0x0003	/* DES cbc mode with RSA-MD5 */
+#define KRB5_ENCTYPE_DES_CBC_RAW		0x0004	/* DES cbc mode raw */
+/* XXX deprecated? */
+#define KRB5_ENCTYPE_DES3_CBC_SHA		0x0005	/* DES-3 cbc mode with NIST-SHA */
+#define KRB5_ENCTYPE_DES3_CBC_RAW		0x0006	/* DES-3 cbc mode raw */
+#define KRB5_ENCTYPE_DES_HMAC_SHA1		0x0008
+#define KRB5_ENCTYPE_DES3_CBC_SHA1		0x0010
+#define KRB5_ENCTYPE_AES128_CTS_HMAC_SHA1_96	0x0011
+#define KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96	0x0012
+#define KRB5_ENCTYPE_ARCFOUR_HMAC		0x0017
+#define KRB5_ENCTYPE_ARCFOUR_HMAC_EXP		0x0018
+#define KRB5_ENCTYPE_UNKNOWN			0x01ff
+
+#define KRB5_CKSUMTYPE_CRC32			0x0001
+#define KRB5_CKSUMTYPE_RSA_MD4			0x0002
+#define KRB5_CKSUMTYPE_RSA_MD4_DES		0x0003
+#define KRB5_CKSUMTYPE_DESCBC			0x0004
+#define KRB5_CKSUMTYPE_RSA_MD5			0x0007
+#define KRB5_CKSUMTYPE_RSA_MD5_DES		0x0008
+#define KRB5_CKSUMTYPE_NIST_SHA			0x0009
+#define KRB5_CKSUMTYPE_HMAC_SHA1_DES3		0x000c
+#define KRB5_CKSUMTYPE_HMAC_SHA1_96_AES128	0x000f
+#define KRB5_CKSUMTYPE_HMAC_SHA1_96_AES256	0x0010
+#define KRB5_CKSUMTYPE_HMAC_MD5_ARCFOUR		-138 /* Microsoft md5 hmac cksumtype */
+
+/*
+ * Constants used for key derivation
+ */
+/* from rfc3961 */
+#define KEY_USAGE_SEED_CHECKSUM         (0x99)
+#define KEY_USAGE_SEED_ENCRYPTION       (0xAA)
+#define KEY_USAGE_SEED_INTEGRITY        (0x55)
+
+#endif /* _CRYPTO_KRB5_H */



From dhowells at redhat.com  Fri Jan 17 10:35:12 2025
From: dhowells at redhat.com (David Howells)
Date: Fri, 17 Jan 2025 18:35:12 +0000
Subject: [RFC PATCH 03/24] crypto: Add 'krb5enc' hash and cipher AEAD algorithm
In-Reply-To: <20250117183538.881618-1-dhowells@redhat.com>
References: <20250117183538.881618-1-dhowells@redhat.com>
Message-ID: <20250117183538.881618-4-dhowells@redhat.com>

Add an AEAD template that does hash-then-cipher (unlike authenc that does
cipher-then-hash).  This is required for a number of Kerberos 5 encoding
types.

[!] Note that the net/sunrpc/auth_gss/ implementation gets a pair of
ciphers, one non-CTS and one CTS, using the former to do all the aligned
blocks and the latter to do the last two blocks if they aren't also
aligned.  It may be necessary to do this here too for performance reasons -
but there are considerations both ways:

 (1) firstly, there is an optimised assembly version of cts(cbc(aes)) on
     x86_64 that should be used instead of having two ciphers;

 (2) secondly, none of the hardware offload drivers seem to offer CTS
     support (Intel QAT does not, for instance).

However, I don't know if it's possible to query the crypto API to find out
whether there's an optimised CTS algorithm available.

Signed-off-by: David Howells <dhowells at redhat.com>
cc: Herbert Xu <herbert at gondor.apana.org.au>
cc: "David S. Miller" <davem at davemloft.net>
cc: Chuck Lever <chuck.lever at oracle.com>
cc: Marc Dionne <marc.dionne at auristor.com>
cc: Eric Dumazet <edumazet at google.com>
cc: Jakub Kicinski <kuba at kernel.org>
cc: Paolo Abeni <pabeni at redhat.com>
cc: Simon Horman <horms at kernel.org>
cc: linux-afs at lists.infradead.org
cc: linux-nfs at vger.kernel.org
cc: linux-crypto at vger.kernel.org
cc: netdev at vger.kernel.org
---
 crypto/Kconfig        |  12 ++
 crypto/Makefile       |   1 +
 crypto/krb5enc.c      | 491 ++++++++++++++++++++++++++++++++++++++++++
 include/crypto/krb5.h |   7 +
 4 files changed, 511 insertions(+)
 create mode 100644 crypto/krb5enc.c

diff --git a/crypto/Kconfig b/crypto/Kconfig
index 6b0bfbccac08..18b1a3b3a258 100644
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -228,6 +228,18 @@ config CRYPTO_AUTHENC
 
 	  This is required for IPSec ESP (XFRM_ESP).
 
+config CRYPTO_KRB5ENC
+	tristate "Kerberos 5 combined hash+cipher support"
+	select CRYPTO_AEAD
+	select CRYPTO_SKCIPHER
+	select CRYPTO_MANAGER
+	select CRYPTO_HASH
+	select CRYPTO_NULL
+	help
+	  Combined hash and cipher support for Kerberos 5 RFC3961 simplified
+	  profile.  This is required for Kerberos 5-style encryption, used by
+	  sunrpc/NFS and rxrpc/AFS.
+
 config CRYPTO_TEST
 	tristate "Testing module"
 	depends on m || EXPERT
diff --git a/crypto/Makefile b/crypto/Makefile
index 77abca715445..eb40638c6c04 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -160,6 +160,7 @@ CFLAGS_crc32_generic.o += -DARCH=$(ARCH)
 obj-$(CONFIG_CRYPTO_CRCT10DIF) += crct10dif_common.o crct10dif_generic.o
 obj-$(CONFIG_CRYPTO_CRC64_ROCKSOFT) += crc64_rocksoft_generic.o
 obj-$(CONFIG_CRYPTO_AUTHENC) += authenc.o authencesn.o
+obj-$(CONFIG_CRYPTO_KRB5ENC) += krb5enc.o
 obj-$(CONFIG_CRYPTO_LZO) += lzo.o lzo-rle.o
 obj-$(CONFIG_CRYPTO_LZ4) += lz4.o
 obj-$(CONFIG_CRYPTO_LZ4HC) += lz4hc.o
diff --git a/crypto/krb5enc.c b/crypto/krb5enc.c
new file mode 100644
index 000000000000..931387a8ee6f
--- /dev/null
+++ b/crypto/krb5enc.c
@@ -0,0 +1,491 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * AEAD wrapper for Kerberos 5 RFC3961 simplified profile.
+ *
+ * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells at redhat.com)
+ *
+ * Derived from authenc:
+ * Copyright (c) 2007-2015 Herbert Xu <herbert at gondor.apana.org.au>
+ */
+
+#include <crypto/internal/aead.h>
+#include <crypto/internal/hash.h>
+#include <crypto/internal/skcipher.h>
+#include <crypto/krb5.h>
+#include <crypto/authenc.h>
+#include <crypto/scatterwalk.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/rtnetlink.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+struct krb5enc_instance_ctx {
+	struct crypto_ahash_spawn auth;
+	struct crypto_skcipher_spawn enc;
+	unsigned int reqoff;
+};
+
+struct krb5enc_ctx {
+	struct crypto_ahash *auth;
+	struct crypto_skcipher *enc;
+};
+
+struct krb5enc_request_ctx {
+	struct scatterlist src[2];
+	struct scatterlist dst[2];
+	char tail[];
+};
+
+static void krb5enc_request_complete(struct aead_request *req, int err)
+{
+	if (err != -EINPROGRESS)
+		aead_request_complete(req, err);
+}
+
+/**
+ * crypto_krb5enc_extractkeys - Extract Ke and Ki keys from the key blob.
+ * @keys: Where to put the key sizes and pointers
+ * @key: Encoded key material
+ * @keylen: Amount of key material
+ *
+ * Decode the key blob we're given.  It starts with a __be32 that indicates the
+ * format.  Format 1 is:
+ *
+ *  be32 1 || be32 Ke len || be32 Ki len || Ke || Ki
+ */
+int crypto_krb5enc_extractkeys(struct crypto_authenc_keys *keys, const u8 *key,
+			       unsigned int keylen)
+{
+	__be32 *k = (void *)key;
+	u32 format, Ke_len, Ki_len;
+
+	if (keylen < 16)
+		return -EINVAL;
+
+	format = get_unaligned_be32(k++);
+	if (format != 1)
+		return -EINVAL;
+	Ke_len = get_unaligned_be32(k++);
+	Ki_len = get_unaligned_be32(k++);
+	keylen -= 12;
+
+	if (Ke_len + Ki_len != keylen)
+		return -EINVAL;
+
+	keys->enckeylen		= Ke_len;
+	keys->enckey		= (void *)k;
+	keys->authkeylen	= Ki_len;
+	keys->authkey		= (void *)k + Ke_len;
+	return 0;
+}
+EXPORT_SYMBOL(crypto_krb5enc_extractkeys);
+
+static int krb5enc_setkey(struct crypto_aead *krb5enc, const u8 *key,
+			  unsigned int keylen)
+{
+	struct crypto_authenc_keys keys;
+	struct krb5enc_ctx *ctx = crypto_aead_ctx(krb5enc);
+	struct crypto_skcipher *enc = ctx->enc;
+	struct crypto_ahash *auth = ctx->auth;
+	unsigned int flags = crypto_aead_get_flags(krb5enc);
+	int err = -EINVAL;
+
+	if (crypto_krb5enc_extractkeys(&keys, key, keylen) != 0)
+		goto out;
+
+	crypto_ahash_clear_flags(auth, CRYPTO_TFM_REQ_MASK);
+	crypto_ahash_set_flags(auth, flags & CRYPTO_TFM_REQ_MASK);
+	err = crypto_ahash_setkey(auth, keys.authkey, keys.authkeylen);
+	if (err)
+		goto out;
+
+	crypto_skcipher_clear_flags(enc, CRYPTO_TFM_REQ_MASK);
+	crypto_skcipher_set_flags(enc, flags & CRYPTO_TFM_REQ_MASK);
+	err = crypto_skcipher_setkey(enc, keys.enckey, keys.enckeylen);
+out:
+	memzero_explicit(&keys, sizeof(keys));
+	return err;
+}
+
+static void krb5enc_encrypt_done(void *data, int err)
+{
+	struct aead_request *req = data;
+
+	krb5enc_request_complete(req, err);
+}
+
+/*
+ * Start the encryption of the plaintext.  We skip over the associated data as
+ * that only gets included in the hash.
+ */
+static int krb5enc_dispatch_encrypt(struct aead_request *req,
+				    unsigned int flags)
+{
+	struct crypto_aead *krb5enc = crypto_aead_reqtfm(req);
+	struct aead_instance *inst = aead_alg_instance(krb5enc);
+	struct krb5enc_ctx *ctx = crypto_aead_ctx(krb5enc);
+	struct krb5enc_instance_ctx *ictx = aead_instance_ctx(inst);
+	struct krb5enc_request_ctx *areq_ctx = aead_request_ctx(req);
+	struct crypto_skcipher *enc = ctx->enc;
+	struct skcipher_request *skreq = (void *)(areq_ctx->tail +
+						  ictx->reqoff);
+	struct scatterlist *src, *dst;
+
+	src = scatterwalk_ffwd(areq_ctx->src, req->src, req->assoclen);
+	if (req->src == req->dst)
+		dst = src;
+	else
+		dst = scatterwalk_ffwd(areq_ctx->dst, req->dst, req->assoclen);
+
+	skcipher_request_set_tfm(skreq, enc);
+	skcipher_request_set_callback(skreq, aead_request_flags(req),
+				      krb5enc_encrypt_done, req);
+	skcipher_request_set_crypt(skreq, src, dst, req->cryptlen, req->iv);
+
+	return crypto_skcipher_encrypt(skreq);
+}
+
+/*
+ * Insert the hash into the checksum field in the destination buffer directly
+ * after the encrypted region.
+ */
+static void krb5enc_insert_checksum(struct aead_request *req, u8 *hash)
+{
+	struct crypto_aead *krb5enc = crypto_aead_reqtfm(req);
+
+	scatterwalk_map_and_copy(hash, req->dst,
+				 req->assoclen + req->cryptlen,
+				 crypto_aead_authsize(krb5enc), 1);
+}
+
+/*
+ * Upon completion of an asynchronous digest, transfer the hash to the checksum
+ * field.
+ */
+static void krb5enc_encrypt_ahash_done(void *data, int err)
+{
+	struct aead_request *req = data;
+	struct crypto_aead *krb5enc = crypto_aead_reqtfm(req);
+	struct aead_instance *inst = aead_alg_instance(krb5enc);
+	struct krb5enc_instance_ctx *ictx = aead_instance_ctx(inst);
+	struct krb5enc_request_ctx *areq_ctx = aead_request_ctx(req);
+	struct ahash_request *ahreq = (void *)(areq_ctx->tail + ictx->reqoff);
+
+	if (err)
+		return krb5enc_request_complete(req, err);
+
+	krb5enc_insert_checksum(req, ahreq->result);
+
+	err = krb5enc_dispatch_encrypt(req, 0);
+	if (err != -EINPROGRESS)
+		aead_request_complete(req, err);
+}
+
+/*
+ * Start the digest of the plaintext for encryption.  In theory, this could be
+ * run in parallel with the encryption, provided the src and dst buffers don't
+ * overlap.
+ */
+static int krb5enc_dispatch_encrypt_hash(struct aead_request *req)
+{
+	struct crypto_aead *krb5enc = crypto_aead_reqtfm(req);
+	struct aead_instance *inst = aead_alg_instance(krb5enc);
+	struct krb5enc_ctx *ctx = crypto_aead_ctx(krb5enc);
+	struct krb5enc_instance_ctx *ictx = aead_instance_ctx(inst);
+	struct crypto_ahash *auth = ctx->auth;
+	struct krb5enc_request_ctx *areq_ctx = aead_request_ctx(req);
+	struct ahash_request *ahreq = (void *)(areq_ctx->tail + ictx->reqoff);
+	u8 *hash = areq_ctx->tail;
+	int err;
+
+	ahash_request_set_callback(ahreq, aead_request_flags(req),
+				   krb5enc_encrypt_ahash_done, req);
+	ahash_request_set_tfm(ahreq, auth);
+	ahash_request_set_crypt(ahreq, req->src, hash, req->assoclen + req->cryptlen);
+
+	err = crypto_ahash_digest(ahreq);
+	if (err)
+		return err;
+
+	krb5enc_insert_checksum(req, hash);
+	return 0;
+}
+
+/*
+ * Process an encryption operation.  We can perform the cipher and the hash in
+ * parallel, provided the src and dst buffers are separate.
+ */
+static int krb5enc_encrypt(struct aead_request *req)
+{
+	int err;
+
+	err = krb5enc_dispatch_encrypt_hash(req);
+	if (err < 0)
+		return err;
+
+	return krb5enc_dispatch_encrypt(req, aead_request_flags(req));
+}
+
+static int krb5enc_verify_hash(struct aead_request *req, void *hash)
+{
+	struct crypto_aead *krb5enc = crypto_aead_reqtfm(req);
+	struct aead_instance *inst = aead_alg_instance(krb5enc);
+	struct krb5enc_instance_ctx *ictx = aead_instance_ctx(inst);
+	struct krb5enc_request_ctx *areq_ctx = aead_request_ctx(req);
+	struct ahash_request *ahreq = (void *)(areq_ctx->tail + ictx->reqoff);
+	unsigned int authsize = crypto_aead_authsize(krb5enc);
+	u8 *ihash = ahreq->result + authsize;
+
+	scatterwalk_map_and_copy(ihash, req->src, ahreq->nbytes, authsize, 0);
+
+	if (crypto_memneq(ihash, ahreq->result, authsize))
+		return -EBADMSG;
+	return 0;
+}
+
+static void krb5enc_decrypt_hash_done(void *data, int err)
+{
+	struct aead_request *req = data;
+
+	if (err)
+		return krb5enc_request_complete(req, err);
+
+	err = krb5enc_verify_hash(req, 0);
+	krb5enc_request_complete(req, err);
+}
+
+/*
+ * Dispatch the hashing of the plaintext after we've done the decryption.
+ */
+static int krb5enc_dispatch_decrypt_hash(struct aead_request *req)
+{
+	struct crypto_aead *krb5enc = crypto_aead_reqtfm(req);
+	struct aead_instance *inst = aead_alg_instance(krb5enc);
+	struct krb5enc_ctx *ctx = crypto_aead_ctx(krb5enc);
+	struct krb5enc_instance_ctx *ictx = aead_instance_ctx(inst);
+	struct krb5enc_request_ctx *areq_ctx = aead_request_ctx(req);
+	struct ahash_request *ahreq = (void *)(areq_ctx->tail + ictx->reqoff);
+	struct crypto_ahash *auth = ctx->auth;
+	unsigned int authsize = crypto_aead_authsize(krb5enc);
+	u8 *hash = areq_ctx->tail;
+	int err;
+
+	ahash_request_set_tfm(ahreq, auth);
+	ahash_request_set_crypt(ahreq, req->dst, hash,
+				req->assoclen + req->cryptlen - authsize);
+	ahash_request_set_callback(ahreq, aead_request_flags(req),
+				   krb5enc_decrypt_hash_done, req);
+
+	err = crypto_ahash_digest(ahreq);
+	if (err < 0)
+		return err;
+
+	return krb5enc_verify_hash(req, hash);
+}
+
+/*
+ * Dispatch the decryption of the ciphertext.
+ */
+static int krb5enc_dispatch_decrypt(struct aead_request *req)
+{
+	struct crypto_aead *krb5enc = crypto_aead_reqtfm(req);
+	struct aead_instance *inst = aead_alg_instance(krb5enc);
+	struct krb5enc_ctx *ctx = crypto_aead_ctx(krb5enc);
+	struct krb5enc_instance_ctx *ictx = aead_instance_ctx(inst);
+	struct krb5enc_request_ctx *areq_ctx = aead_request_ctx(req);
+	struct skcipher_request *skreq = (void *)(areq_ctx->tail +
+						  ictx->reqoff);
+	unsigned int authsize = crypto_aead_authsize(krb5enc);
+	struct scatterlist *src, *dst;
+
+	src = scatterwalk_ffwd(areq_ctx->src, req->src, req->assoclen);
+	dst = src;
+
+	if (req->src != req->dst)
+		dst = scatterwalk_ffwd(areq_ctx->dst, req->dst, req->assoclen);
+
+	skcipher_request_set_tfm(skreq, ctx->enc);
+	skcipher_request_set_callback(skreq, aead_request_flags(req),
+				      req->base.complete, req->base.data);
+	skcipher_request_set_crypt(skreq, src, dst,
+				   req->cryptlen - authsize, req->iv);
+
+	return crypto_skcipher_decrypt(skreq);
+}
+
+static int krb5enc_decrypt(struct aead_request *req)
+{
+	int err;
+
+	err = krb5enc_dispatch_decrypt(req);
+	if (err < 0)
+		return err;
+
+	return krb5enc_dispatch_decrypt_hash(req);
+}
+
+static int krb5enc_init_tfm(struct crypto_aead *tfm)
+{
+	struct aead_instance *inst = aead_alg_instance(tfm);
+	struct krb5enc_instance_ctx *ictx = aead_instance_ctx(inst);
+	struct krb5enc_ctx *ctx = crypto_aead_ctx(tfm);
+	struct crypto_ahash *auth;
+	struct crypto_skcipher *enc;
+	int err;
+
+	auth = crypto_spawn_ahash(&ictx->auth);
+	if (IS_ERR(auth))
+		return PTR_ERR(auth);
+
+	enc = crypto_spawn_skcipher(&ictx->enc);
+	err = PTR_ERR(enc);
+	if (IS_ERR(enc))
+		goto err_free_ahash;
+
+	ctx->auth = auth;
+	ctx->enc = enc;
+
+	crypto_aead_set_reqsize(
+		tfm,
+		sizeof(struct krb5enc_request_ctx) +
+		ictx->reqoff +
+		umax(sizeof(struct ahash_request) + crypto_ahash_reqsize(auth),
+		     sizeof(struct skcipher_request) + crypto_skcipher_reqsize(enc)));
+
+	return 0;
+
+err_free_ahash:
+	crypto_free_ahash(auth);
+	return err;
+}
+
+static void krb5enc_exit_tfm(struct crypto_aead *tfm)
+{
+	struct krb5enc_ctx *ctx = crypto_aead_ctx(tfm);
+
+	crypto_free_ahash(ctx->auth);
+	crypto_free_skcipher(ctx->enc);
+}
+
+static void krb5enc_free(struct aead_instance *inst)
+{
+	struct krb5enc_instance_ctx *ctx = aead_instance_ctx(inst);
+
+	crypto_drop_skcipher(&ctx->enc);
+	crypto_drop_ahash(&ctx->auth);
+	kfree(inst);
+}
+
+/*
+ * Create an instance of a template for a specific hash and cipher pair.
+ */
+static int krb5enc_create(struct crypto_template *tmpl, struct rtattr **tb)
+{
+	struct krb5enc_instance_ctx *ictx;
+	struct skcipher_alg_common *enc;
+	struct hash_alg_common *auth;
+	struct aead_instance *inst;
+	struct crypto_alg *auth_base;
+	u32 mask;
+	int err;
+
+	err = crypto_check_attr_type(tb, CRYPTO_ALG_TYPE_AEAD, &mask);
+	if (err) {
+		pr_err("attr_type failed\n");
+		return err;
+	}
+
+	inst = kzalloc(sizeof(*inst) + sizeof(*ictx), GFP_KERNEL);
+	if (!inst)
+		return -ENOMEM;
+	ictx = aead_instance_ctx(inst);
+
+	err = crypto_grab_ahash(&ictx->auth, aead_crypto_instance(inst),
+				crypto_attr_alg_name(tb[1]), 0, mask);
+	if (err) {
+		pr_err("grab ahash failed\n");
+		goto err_free_inst;
+	}
+	auth = crypto_spawn_ahash_alg(&ictx->auth);
+	auth_base = &auth->base;
+
+	err = crypto_grab_skcipher(&ictx->enc, aead_crypto_instance(inst),
+				   crypto_attr_alg_name(tb[2]), 0, mask);
+	if (err) {
+		pr_err("grab skcipher failed\n");
+		goto err_free_inst;
+	}
+	enc = crypto_spawn_skcipher_alg_common(&ictx->enc);
+
+	ictx->reqoff = 2 * auth->digestsize;
+
+	err = -ENAMETOOLONG;
+	if (snprintf(inst->alg.base.cra_name, CRYPTO_MAX_ALG_NAME,
+		     "krb5enc(%s,%s)", auth_base->cra_name,
+		     enc->base.cra_name) >=
+	    CRYPTO_MAX_ALG_NAME)
+		goto err_free_inst;
+
+	if (snprintf(inst->alg.base.cra_driver_name, CRYPTO_MAX_ALG_NAME,
+		     "krb5enc(%s,%s)", auth_base->cra_driver_name,
+		     enc->base.cra_driver_name) >= CRYPTO_MAX_ALG_NAME)
+		goto err_free_inst;
+
+	inst->alg.base.cra_priority = enc->base.cra_priority * 10 +
+				      auth_base->cra_priority;
+	inst->alg.base.cra_blocksize = enc->base.cra_blocksize;
+	inst->alg.base.cra_alignmask = enc->base.cra_alignmask;
+	inst->alg.base.cra_ctxsize = sizeof(struct krb5enc_ctx);
+
+	inst->alg.ivsize = enc->ivsize;
+	inst->alg.chunksize = enc->chunksize;
+	inst->alg.maxauthsize = auth->digestsize;
+
+	inst->alg.init = krb5enc_init_tfm;
+	inst->alg.exit = krb5enc_exit_tfm;
+
+	inst->alg.setkey = krb5enc_setkey;
+	inst->alg.encrypt = krb5enc_encrypt;
+	inst->alg.decrypt = krb5enc_decrypt;
+
+	inst->free = krb5enc_free;
+
+	err = aead_register_instance(tmpl, inst);
+	if (err) {
+		pr_err("ref failed\n");
+		goto err_free_inst;
+	}
+
+	return 0;
+
+err_free_inst:
+	krb5enc_free(inst);
+	return err;
+}
+
+static struct crypto_template crypto_krb5enc_tmpl = {
+	.name = "krb5enc",
+	.create = krb5enc_create,
+	.module = THIS_MODULE,
+};
+
+static int __init crypto_krb5enc_module_init(void)
+{
+	return crypto_register_template(&crypto_krb5enc_tmpl);
+}
+
+static void __exit crypto_krb5enc_module_exit(void)
+{
+	crypto_unregister_template(&crypto_krb5enc_tmpl);
+}
+
+subsys_initcall(crypto_krb5enc_module_init);
+module_exit(crypto_krb5enc_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Simple AEAD wrapper for Kerberos 5 RFC3961");
+MODULE_ALIAS_CRYPTO("krb5enc");
diff --git a/include/crypto/krb5.h b/include/crypto/krb5.h
index 44a6342471d7..8949a9b71de3 100644
--- a/include/crypto/krb5.h
+++ b/include/crypto/krb5.h
@@ -48,4 +48,11 @@
 #define KEY_USAGE_SEED_ENCRYPTION       (0xAA)
 #define KEY_USAGE_SEED_INTEGRITY        (0x55)
 
+/*
+ * krb5enc.c
+ */
+struct crypto_authenc_keys;
+int crypto_krb5enc_extractkeys(struct crypto_authenc_keys *keys, const u8 *key,
+			       unsigned int keylen);
+
 #endif /* _CRYPTO_KRB5_H */



From dhowells at redhat.com  Fri Jan 17 10:35:13 2025
From: dhowells at redhat.com (David Howells)
Date: Fri, 17 Jan 2025 18:35:13 +0000
Subject: [RFC PATCH 04/24] crypto/krb5: Test manager data
In-Reply-To: <20250117183538.881618-1-dhowells@redhat.com>
References: <20250117183538.881618-1-dhowells@redhat.com>
Message-ID: <20250117183538.881618-5-dhowells@redhat.com>

Add Kerberos crypto tests to the test manager database.  This covers:

	camellia128-cts-cmac		samples from RFC6803
	camellia256-cts-cmac		samples from RFC6803
	aes128-cts-hmac-sha256-128	samples from RFC8009
	aes256-cts-hmac-sha384-192	samples from RFC8009

but not:

	aes128-cts-hmac-sha1-96
	aes256-cts-hmac-sha1-96

as the test samples in RFC3962 don't seem to be suitable.

Signed-off-by: David Howells <dhowells at redhat.com>
cc: Herbert Xu <herbert at gondor.apana.org.au>
cc: "David S. Miller" <davem at davemloft.net>
cc: Chuck Lever <chuck.lever at oracle.com>
cc: Marc Dionne <marc.dionne at auristor.com>
cc: Eric Dumazet <edumazet at google.com>
cc: Jakub Kicinski <kuba at kernel.org>
cc: Paolo Abeni <pabeni at redhat.com>
cc: Simon Horman <horms at kernel.org>
cc: linux-afs at lists.infradead.org
cc: linux-nfs at vger.kernel.org
cc: linux-crypto at vger.kernel.org
cc: netdev at vger.kernel.org
---
 crypto/testmgr.c |  16 ++
 crypto/testmgr.h | 401 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 417 insertions(+)

diff --git a/crypto/testmgr.c b/crypto/testmgr.c
index 1f5f48ab18c7..94d70bf39de9 100644
--- a/crypto/testmgr.c
+++ b/crypto/testmgr.c
@@ -4515,6 +4515,12 @@ static const struct alg_test_desc alg_test_descs[] = {
 		.alg = "authenc(hmac(sha256),ctr(aes))",
 		.test = alg_test_null,
 		.fips_allowed = 1,
+	}, {
+		.alg = "authenc(hmac(sha256),cts(cbc(aes)))",
+		.test = alg_test_aead,
+		.suite = {
+			.aead = __VECS(krb5_test_aes128_cts_hmac_sha256_128)
+		}
 	}, {
 		.alg = "authenc(hmac(sha256),rfc3686(ctr(aes)))",
 		.test = alg_test_null,
@@ -4535,6 +4541,12 @@ static const struct alg_test_desc alg_test_descs[] = {
 		.alg = "authenc(hmac(sha384),ctr(aes))",
 		.test = alg_test_null,
 		.fips_allowed = 1,
+	}, {
+		.alg = "authenc(hmac(sha384),cts(cbc(aes)))",
+		.test = alg_test_aead,
+		.suite = {
+			.aead = __VECS(krb5_test_aes256_cts_hmac_sha384_192)
+		}
 	}, {
 		.alg = "authenc(hmac(sha384),rfc3686(ctr(aes)))",
 		.test = alg_test_null,
@@ -5408,6 +5420,10 @@ static const struct alg_test_desc alg_test_descs[] = {
 		.alg = "jitterentropy_rng",
 		.fips_allowed = 1,
 		.test = alg_test_null,
+	}, {
+		.alg = "krb5enc(cmac(camellia),cts(cbc(camellia)))",
+		.test = alg_test_aead,
+		.suite.aead = __VECS(krb5_test_camellia_cts_cmac)
 	}, {
 		.alg = "kw(aes)",
 		.test = alg_test_skcipher,
diff --git a/crypto/testmgr.h b/crypto/testmgr.h
index 430d33d9ac13..c1fa0e6e8108 100644
--- a/crypto/testmgr.h
+++ b/crypto/testmgr.h
@@ -39086,4 +39086,405 @@ static const struct cipher_testvec aes_hctr2_tv_template[] = {
 
 };
 
+static const struct aead_testvec krb5_test_aes128_cts_hmac_sha256_128[] = {
+	/* rfc8009 Appendix A */
+	{
+		/* "enc no plain" */
+		.key	=
+#ifdef CONFIG_CPU_BIG_ENDIAN
+		"\x00\x08\x00\x01" // BE rtattr
+#else
+		"\x08\x00\x01\x00" // LE rtattr
+#endif
+		"\x00\x00\x00\x10" // crypto_authenc_key_param
+		"\x9F\xDA\x0E\x56\xAB\x2D\x85\xE1\x56\x9A\x68\x86\x96\xC2\x6A\x6C" // Ki
+		"\x9B\x19\x7D\xD1\xE8\xC5\x60\x9D\x6E\x67\xC3\xE3\x7C\x62\xC7\x2E", // Ke
+		.klen	= 4 + 4 + 16 + 16,
+		.ptext	=
+		"\x7E\x58\x95\xEA\xF2\x67\x24\x35\xBA\xD8\x17\xF5\x45\xA3\x71\x48" // Confounder
+		"", // Plain
+		.plen	= 16 + 0,
+		.ctext	=
+		"\xEF\x85\xFB\x89\x0B\xB8\x47\x2F\x4D\xAB\x20\x39\x4D\xCA\x78\x1D"
+		"\xAD\x87\x7E\xDA\x39\xD5\x0C\x87\x0C\x0D\x5A\x0A\x8E\x48\xC7\x18",
+		.clen	= 16 + 0 + 16,
+		.assoc	= "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // IV
+		.alen	= 16,
+	}, {
+		/* "enc plain<block" */
+		.key	=
+#ifdef CONFIG_CPU_BIG_ENDIAN
+		"\x00\x08\x00\x01" // BE rtattr
+#else
+		"\x08\x00\x01\x00" // LE rtattr
+#endif
+		"\x00\x00\x00\x10" // crypto_authenc_key_param
+		"\x9F\xDA\x0E\x56\xAB\x2D\x85\xE1\x56\x9A\x68\x86\x96\xC2\x6A\x6C" // Ki
+		"\x9B\x19\x7D\xD1\xE8\xC5\x60\x9D\x6E\x67\xC3\xE3\x7C\x62\xC7\x2E", // Ke
+		.klen	= 4 + 4 + 16 + 16,
+		.ptext	=
+		"\x7B\xCA\x28\x5E\x2F\xD4\x13\x0F\xB5\x5B\x1A\x5C\x83\xBC\x5B\x24" // Confounder
+		"\x00\x01\x02\x03\x04\x05", // Plain
+		.plen	= 16 + 6,
+		.ctext	=
+		"\x84\xD7\xF3\x07\x54\xED\x98\x7B\xAB\x0B\xF3\x50\x6B\xEB\x09\xCF"
+		"\xB5\x54\x02\xCE\xF7\xE6\x87\x7C\xE9\x9E\x24\x7E\x52\xD1\x6E\xD4"
+		"\x42\x1D\xFD\xF8\x97\x6C",
+		.clen	= 16 + 6 + 16,
+		.assoc	= "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // IV
+		.alen	= 16,
+	}, {
+		/* "enc plain==block" */
+		.key	=
+#ifdef CONFIG_CPU_BIG_ENDIAN
+		"\x00\x08\x00\x01" // BE rtattr
+#else
+		"\x08\x00\x01\x00" // LE rtattr
+#endif
+		"\x00\x00\x00\x10" // crypto_authenc_key_param
+		"\x9F\xDA\x0E\x56\xAB\x2D\x85\xE1\x56\x9A\x68\x86\x96\xC2\x6A\x6C" // Ki
+		"\x9B\x19\x7D\xD1\xE8\xC5\x60\x9D\x6E\x67\xC3\xE3\x7C\x62\xC7\x2E", // Ke
+		.klen	= 4 + 4 + 16 + 16,
+		.ptext	=
+		"\x56\xAB\x21\x71\x3F\xF6\x2C\x0A\x14\x57\x20\x0F\x6F\xA9\x94\x8F" // Confounder
+		"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", // Plain
+		.plen	= 16 + 16,
+		.ctext	=
+		"\x35\x17\xD6\x40\xF5\x0D\xDC\x8A\xD3\x62\x87\x22\xB3\x56\x9D\x2A"
+		"\xE0\x74\x93\xFA\x82\x63\x25\x40\x80\xEA\x65\xC1\x00\x8E\x8F\xC2"
+		"\x95\xFB\x48\x52\xE7\xD8\x3E\x1E\x7C\x48\xC3\x7E\xEB\xE6\xB0\xD3",
+		.clen	= 16 + 16 + 16,
+		.assoc	= "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // IV
+		.alen	= 16,
+	}, {
+		/* "enc plain>block" */
+		.key	=
+#ifdef CONFIG_CPU_BIG_ENDIAN
+		"\x00\x08\x00\x01" // BE rtattr
+#else
+		"\x08\x00\x01\x00" // LE rtattr
+#endif
+		"\x00\x00\x00\x10" // crypto_authenc_key_param
+		"\x9F\xDA\x0E\x56\xAB\x2D\x85\xE1\x56\x9A\x68\x86\x96\xC2\x6A\x6C" // Ki
+		"\x9B\x19\x7D\xD1\xE8\xC5\x60\x9D\x6E\x67\xC3\xE3\x7C\x62\xC7\x2E", // Ke
+		.klen	= 4 + 4 + 16 + 16,
+		.ptext	=
+		"\xA7\xA4\xE2\x9A\x47\x28\xCE\x10\x66\x4F\xB6\x4E\x49\xAD\x3F\xAC" // Confounder
+		"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
+		"\x10\x11\x12\x13\x14", // Plain
+		.plen	= 16 + 21,
+		.ctext	=
+		"\x72\x0F\x73\xB1\x8D\x98\x59\xCD\x6C\xCB\x43\x46\x11\x5C\xD3\x36"
+		"\xC7\x0F\x58\xED\xC0\xC4\x43\x7C\x55\x73\x54\x4C\x31\xC8\x13\xBC"
+		"\xE1\xE6\xD0\x72\xC1\x86\xB3\x9A\x41\x3C\x2F\x92\xCA\x9B\x83\x34"
+		"\xA2\x87\xFF\xCB\xFC",
+		.clen	= 16 + 21 + 16,
+		.assoc	= "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // IV
+		.alen	= 16,
+	},
+};
+
+static const struct aead_testvec krb5_test_aes256_cts_hmac_sha384_192[] = {
+	/* rfc8009 Appendix A */
+	{
+		/* "enc no plain" */
+		.key	=
+#ifdef CONFIG_CPU_BIG_ENDIAN
+		"\x00\x08\x00\x01" // BE rtattr
+#else
+		"\x08\x00\x01\x00" // LE rtattr
+#endif
+		"\x00\x00\x00\x20" // crypto_authenc_key_param
+		"\x69\xB1\x65\x14\xE3\xCD\x8E\x56\xB8\x20\x10\xD5\xC7\x30\x12\xB6"
+		"\x22\xC4\xD0\x0F\xFC\x23\xED\x1F" // Ki
+		"\x56\xAB\x22\xBE\xE6\x3D\x82\xD7\xBC\x52\x27\xF6\x77\x3F\x8E\xA7"
+		"\xA5\xEB\x1C\x82\x51\x60\xC3\x83\x12\x98\x0C\x44\x2E\x5C\x7E\x49", // Ke
+		.klen	= 4 + 4 + 32 + 24,
+		.ptext	=
+		"\xF7\x64\xE9\xFA\x15\xC2\x76\x47\x8B\x2C\x7D\x0C\x4E\x5F\x58\xE4" // Confounder
+		"", // Plain
+		.plen	= 16 + 0,
+		.ctext	=
+		"\x41\xF5\x3F\xA5\xBF\xE7\x02\x6D\x91\xFA\xF9\xBE\x95\x91\x95\xA0"
+		"\x58\x70\x72\x73\xA9\x6A\x40\xF0\xA0\x19\x60\x62\x1A\xC6\x12\x74"
+		"\x8B\x9B\xBF\xBE\x7E\xB4\xCE\x3C",
+		.clen	= 16 + 0 + 24,
+		.assoc	= "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // IV
+		.alen	= 16,
+	}, {
+		/* "enc plain<block" */
+		.key	=
+#ifdef CONFIG_CPU_BIG_ENDIAN
+		"\x00\x08\x00\x01" // BE rtattr
+#else
+		"\x08\x00\x01\x00" // LE rtattr
+#endif
+		"\x00\x00\x00\x20" // crypto_authenc_key_param
+		"\x69\xB1\x65\x14\xE3\xCD\x8E\x56\xB8\x20\x10\xD5\xC7\x30\x12\xB6"
+		"\x22\xC4\xD0\x0F\xFC\x23\xED\x1F" // Ki
+		"\x56\xAB\x22\xBE\xE6\x3D\x82\xD7\xBC\x52\x27\xF6\x77\x3F\x8E\xA7"
+		"\xA5\xEB\x1C\x82\x51\x60\xC3\x83\x12\x98\x0C\x44\x2E\x5C\x7E\x49", // Ke
+		.klen	= 4 + 4 + 32 + 24,
+		.ptext	=
+		"\xB8\x0D\x32\x51\xC1\xF6\x47\x14\x94\x25\x6F\xFE\x71\x2D\x0B\x9A" // Confounder
+		"\x00\x01\x02\x03\x04\x05", // Plain
+		.plen	= 16 + 6,
+		.ctext	=
+		"\x4E\xD7\xB3\x7C\x2B\xCA\xC8\xF7\x4F\x23\xC1\xCF\x07\xE6\x2B\xC7"
+		"\xB7\x5F\xB3\xF6\x37\xB9\xF5\x59\xC7\xF6\x64\xF6\x9E\xAB\x7B\x60"
+		"\x92\x23\x75\x26\xEA\x0D\x1F\x61\xCB\x20\xD6\x9D\x10\xF2",
+		.clen	= 16 + 6 + 24,
+		.assoc	= "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // IV
+		.alen	= 16,
+	}, {
+		/* "enc plain==block" */
+		.key	=
+#ifdef CONFIG_CPU_BIG_ENDIAN
+		"\x00\x08\x00\x01" // BE rtattr
+#else
+		"\x08\x00\x01\x00" // LE rtattr
+#endif
+		"\x00\x00\x00\x20" // crypto_authenc_key_param
+		"\x69\xB1\x65\x14\xE3\xCD\x8E\x56\xB8\x20\x10\xD5\xC7\x30\x12\xB6"
+		"\x22\xC4\xD0\x0F\xFC\x23\xED\x1F" // Ki
+		"\x56\xAB\x22\xBE\xE6\x3D\x82\xD7\xBC\x52\x27\xF6\x77\x3F\x8E\xA7"
+		"\xA5\xEB\x1C\x82\x51\x60\xC3\x83\x12\x98\x0C\x44\x2E\x5C\x7E\x49", // Ke
+		.klen	= 4 + 4 + 32 + 24,
+		.ptext	=
+		"\x53\xBF\x8A\x0D\x10\x52\x65\xD4\xE2\x76\x42\x86\x24\xCE\x5E\x63" // Confounder
+		"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", // Plain
+		.plen	= 16 + 16,
+		.ctext	=
+		"\xBC\x47\xFF\xEC\x79\x98\xEB\x91\xE8\x11\x5C\xF8\xD1\x9D\xAC\x4B"
+		"\xBB\xE2\xE1\x63\xE8\x7D\xD3\x7F\x49\xBE\xCA\x92\x02\x77\x64\xF6"
+		"\x8C\xF5\x1F\x14\xD7\x98\xC2\x27\x3F\x35\xDF\x57\x4D\x1F\x93\x2E"
+		"\x40\xC4\xFF\x25\x5B\x36\xA2\x66",
+		.clen	= 16 + 16 + 24,
+		.assoc	= "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // IV
+		.alen	= 16,
+	}, {
+		/* "enc plain>block" */
+		.key	=
+#ifdef CONFIG_CPU_BIG_ENDIAN
+		"\x00\x08\x00\x01" // BE rtattr
+#else
+		"\x08\x00\x01\x00" // LE rtattr
+#endif
+		"\x00\x00\x00\x20" // crypto_authenc_key_param
+		"\x69\xB1\x65\x14\xE3\xCD\x8E\x56\xB8\x20\x10\xD5\xC7\x30\x12\xB6"
+		"\x22\xC4\xD0\x0F\xFC\x23\xED\x1F" // Ki
+		"\x56\xAB\x22\xBE\xE6\x3D\x82\xD7\xBC\x52\x27\xF6\x77\x3F\x8E\xA7"
+		"\xA5\xEB\x1C\x82\x51\x60\xC3\x83\x12\x98\x0C\x44\x2E\x5C\x7E\x49", // Ke
+		.klen	= 4 + 4 + 32 + 24,
+		.ptext	=
+		"\x76\x3E\x65\x36\x7E\x86\x4F\x02\xF5\x51\x53\xC7\xE3\xB5\x8A\xF1" // Confounder
+		"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
+		"\x10\x11\x12\x13\x14", // Plain
+		.plen	= 16 + 21,
+		.ctext	=
+		"\x40\x01\x3E\x2D\xF5\x8E\x87\x51\x95\x7D\x28\x78\xBC\xD2\xD6\xFE"
+		"\x10\x1C\xCF\xD5\x56\xCB\x1E\xAE\x79\xDB\x3C\x3E\xE8\x64\x29\xF2"
+		"\xB2\xA6\x02\xAC\x86\xFE\xF6\xEC\xB6\x47\xD6\x29\x5F\xAE\x07\x7A"
+		"\x1F\xEB\x51\x75\x08\xD2\xC1\x6B\x41\x92\xE0\x1F\x62",
+		.clen	= 16 + 21 + 24,
+		.assoc	= "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // IV
+		.alen	= 16,
+	},
+};
+
+static const struct aead_testvec krb5_test_camellia_cts_cmac[] = {
+	/* rfc6803 sec 10 */
+	{
+		// "enc no plain"
+		.key	=
+		"\x00\x00\x00\x01" // Format
+		"\x00\x00\x00\x10" // Ke len
+		"\x00\x00\x00\x10" // Ki len
+		"\xe9\x9b\x82\xb3\x6c\x4a\xe8\xea\x19\xe9\x5d\xfa\x9e\xde\x88\x2c" // Ke
+		"\x45\xeb\x66\xe2\xef\xa8\x77\x8f\x7d\xf1\x46\x54\x53\x05\x98\x06", // Ki
+		.klen	= 3 * 4 + 16 * 2,
+		.ptext	=
+		"\xB6\x98\x22\xA1\x9A\x6B\x09\xC0\xEB\xC8\x55\x7D\x1F\x1B\x6C\x0A" // Confounder
+		"", // Plain
+		.plen	= 16 + 0,
+		.ctext	=
+		"\xC4\x66\xF1\x87\x10\x69\x92\x1E\xDB\x7C\x6F\xDE\x24\x4A\x52\xDB"
+		"\x0B\xA1\x0E\xDC\x19\x7B\xDB\x80\x06\x65\x8C\xA3\xCC\xCE\x6E\xB8",
+		.clen	= 16 + 0 + 16,
+	}, {
+		// "enc 1 plain",
+		.key	=
+		"\x00\x00\x00\x01" // Format
+		"\x00\x00\x00\x10" // Ke len
+		"\x00\x00\x00\x10" // Ki len
+		"\xa7\xed\xcd\x53\x97\xea\x6d\x12\xb0\xaf\xf4\xcb\x8d\xaa\x57\xad" // Ke
+		"\x13\x5f\xe7\x11\x6f\x53\xc2\xaa\x36\x12\xb7\xea\xe0\xf2\x84\xaa", // Ki
+		.klen	= 4 * 3 + 16 * 2,
+		.ptext	=
+		"\x6F\x2F\xC3\xC2\xA1\x66\xFD\x88\x98\x96\x7A\x83\xDE\x95\x96\xD9" // Confounder
+		"1", // Plain
+		.plen	= 16 + 1,
+		.ctext	=
+		"\x84\x2D\x21\xFD\x95\x03\x11\xC0\xDD\x46\x4A\x3F\x4B\xE8\xD6\xDA"
+		"\x88\xA5\x6D\x55\x9C\x9B\x47\xD3\xF9\xA8\x50\x67\xAF\x66\x15\x59"
+		"\xB8",
+		.clen	= 16 + 1 + 16,
+	}, {
+		// "enc 9 plain",
+		.key	=
+		"\x00\x00\x00\x01" // Format
+		"\x00\x00\x00\x10" // Ke len
+		"\x00\x00\x00\x10" // Ki len
+		"\xdd\xe4\x2e\xca\x7c\xd9\x86\x3f\xc3\xce\x89\xcb\xc9\x43\x62\xd7" // Ke
+		"\x10\x2c\x34\xd0\x75\x74\x9f\x77\x8a\x15\xca\xd1\xe9\x7d\xa9\x86", // Ki
+		.klen	= 4 * 3 + 16 * 2,
+		.ptext	=
+		"\xA5\xB4\xA7\x1E\x07\x7A\xEE\xF9\x3C\x87\x63\xC1\x8F\xDB\x1F\x10" // Confounder
+		"9 bytesss", // Plain
+		.plen	= 16 + 9,
+		.ctext	=
+		"\x61\x9F\xF0\x72\xE3\x62\x86\xFF\x0A\x28\xDE\xB3\xA3\x52\xEC\x0D"
+		"\x0E\xDF\x5C\x51\x60\xD6\x63\xC9\x01\x75\x8C\xCF\x9D\x1E\xD3\x3D"
+		"\x71\xDB\x8F\x23\xAA\xBF\x83\x48\xA0",
+		.clen	= 16 + 9 + 16,
+	}, {
+		// "enc 13 plain",
+		.key	=
+		"\x00\x00\x00\x01" // Format
+		"\x00\x00\x00\x10" // Ke len
+		"\x00\x00\x00\x10" // Ki len
+		"\xc3\x11\x3a\x25\x85\x90\xb9\xae\xbf\x72\x1b\x1a\xf6\xb0\xcb\xf8" // Ke
+		"\xb8\xc4\x38\xcc\x1a\x00\x60\xfc\x91\x3a\x8e\x07\x16\x96\xbd\x08", // Ki
+		.klen	= 4 * 3 + 16 * 2,
+		.ptext	=
+		"\x19\xFE\xE4\x0D\x81\x0C\x52\x4B\x5B\x22\xF0\x18\x74\xC6\x93\xDA" // Confounder
+		"13 bytes byte", // Plain
+		.plen	= 16 + 13,
+		.ctext	=
+		"\xB8\xEC\xA3\x16\x7A\xE6\x31\x55\x12\xE5\x9F\x98\xA7\xC5\x00\x20"
+		"\x5E\x5F\x63\xFF\x3B\xB3\x89\xAF\x1C\x41\xA2\x1D\x64\x0D\x86\x15"
+		"\xC9\xED\x3F\xBE\xB0\x5A\xB6\xAC\xB6\x76\x89\xB5\xEA",
+		.clen	= 16 + 13 + 16,
+	}, {
+		// "enc 30 plain",
+		.key	=
+		"\x00\x00\x00\x01" // Format
+		"\x00\x00\x00\x10" // Ke len
+		"\x00\x00\x00\x10" // Ki len
+		"\x8b\x07\xee\xd3\x01\x49\x91\x6a\xa2\x0d\xb3\xf5\xce\xd8\xaf\xad" // Ke
+		"\x18\xaf\x19\xb0\x23\x74\x44\xfd\x75\x04\xad\x7d\xbd\x48\xad\xd3", // Ki
+		.klen	= 4 * 3 + 16 * 2,
+		.ptext	=
+		"\xCA\x7A\x7A\xB4\xBE\x19\x2D\xAB\xD6\x03\x50\x6D\xB1\x9C\x39\xE2" // Confounder
+		"30 bytes bytes bytes bytes byt", // Plain
+		.plen	= 16 + 30,
+		.ctext	=
+		"\xA2\x6A\x39\x05\xA4\xFF\xD5\x81\x6B\x7B\x1E\x27\x38\x0D\x08\x09"
+		"\x0C\x8E\xC1\xF3\x04\x49\x6E\x1A\xBD\xCD\x2B\xDC\xD1\xDF\xFC\x66"
+		"\x09\x89\xE1\x17\xA7\x13\xDD\xBB\x57\xA4\x14\x6C\x15\x87\xCB\xA4"
+		"\x35\x66\x65\x59\x1D\x22\x40\x28\x2F\x58\x42\xB1\x05\xA5",
+		.clen	= 16 + 30 + 16,
+	}, {
+		// "enc no plain",
+		.key	=
+		"\x00\x00\x00\x01" // Format
+		"\x00\x00\x00\x20" // Ke len
+		"\x00\x00\x00\x20" // Ki len
+		"\x6c\xcb\x3f\x25\xd8\xae\x57\xf4\xe8\xf6\xca\x47\x4b\xdd\xef\xf1"
+		"\x16\xce\x13\x1b\x3f\x71\x01\x2e\x75\x6d\x6b\x1e\x3f\x70\xa7\xf1" // Ke
+		"\xa2\xb8\x33\xe9\x43\xbb\x10\xee\x53\xb4\xa1\x9b\xc2\xbb\xc7\xe1"
+		"\x9b\x87\xad\x5d\xe9\x21\x22\xa4\x33\x8b\xe6\xf7\x32\xfd\x8a\x0e", // Ki
+		.klen	= 4 * 3 + 32 * 2,
+		.ptext	=
+		"\x3C\xBB\xD2\xB4\x59\x17\x94\x10\x67\xF9\x65\x99\xBB\x98\x92\x6C" // Confounder
+		"", // Plain
+		.plen	= 16 + 0,
+		.ctext	=
+		"\x03\x88\x6D\x03\x31\x0B\x47\xA6\xD8\xF0\x6D\x7B\x94\xD1\xDD\x83"
+		"\x7E\xCC\xE3\x15\xEF\x65\x2A\xFF\x62\x08\x59\xD9\x4A\x25\x92\x66",
+		.clen	= 16 + 0 + 16,
+	}, {
+		// "enc 1 plain",
+		.key	=
+		"\x00\x00\x00\x01" // Format
+		"\x00\x00\x00\x20" // Ke len
+		"\x00\x00\x00\x20" // Ki len
+		"\xe9\x31\x73\xaa\x01\xeb\x3c\x24\x62\x31\xda\xfc\x78\x02\xee\x32"
+		"\xaf\x24\x85\x1d\x8c\x73\x87\xd1\x8c\xb9\xb2\xc5\xb7\xf5\x70\xb8" // Ke
+		"\x84\x61\x4b\xfa\x98\xf1\x74\x8a\xa4\xaf\x99\x2b\x8c\x26\x28\x0d"
+		"\xc8\x98\x73\x29\xdf\x77\x5c\x1d\xb0\x4a\x43\xf1\x21\xaa\x86\x65", // Ki
+		.klen	= 4 * 3 + 32 * 2,
+		.ptext	=
+		"\xDE\xF4\x87\xFC\xEB\xE6\xDE\x63\x46\xD4\xDA\x45\x21\xBB\xA2\xD2" // Confounder
+		"1", // Plain
+		.plen	= 16 + 1,
+		.ctext	=
+		"\x2C\x9C\x15\x70\x13\x3C\x99\xBF\x6A\x34\xBC\x1B\x02\x12\x00\x2F"
+		"\xD1\x94\x33\x87\x49\xDB\x41\x35\x49\x7A\x34\x7C\xFC\xD9\xD1\x8A"
+		"\x12",
+		.clen	= 16 + 1 + 16,
+	}, {
+		// "enc 9 plain",
+		.key	=
+		"\x00\x00\x00\x01" // Format
+		"\x00\x00\x00\x20" // Ke len
+		"\x00\x00\x00\x20" // Ki len
+		"\xcd\xa2\xd3\x9a\x9b\x24\x3f\xfe\xb5\x6e\x8d\x5f\x4b\xd5\x28\x74"
+		"\x1e\xcb\x52\x0c\x62\x12\x3f\xb0\x40\xb8\x41\x8b\x15\xc7\xd7\x0c" // Ke
+		"\x47\xb9\xf5\xba\xd7\x63\x00\x58\x2a\x54\x45\xfa\x0c\x1b\x29\xc3"
+		"\xaa\x83\xec\x63\xb9\x0b\x4a\xb0\x08\x48\xc1\x85\x67\x4f\x44\xa7", // Ki
+		.klen	= 4 * 3 + 32 * 2,
+		.ptext	=
+		"\xAD\x4F\xF9\x04\xD3\x4E\x55\x53\x84\xB1\x41\x00\xFC\x46\x5F\x88" // Confounder
+		"9 bytesss", // Plain
+		.plen	= 16 + 9,
+		.ctext	=
+		"\x9C\x6D\xE7\x5F\x81\x2D\xE7\xED\x0D\x28\xB2\x96\x35\x57\xA1\x15"
+		"\x64\x09\x98\x27\x5B\x0A\xF5\x15\x27\x09\x91\x3F\xF5\x2A\x2A\x9C"
+		"\x8E\x63\xB8\x72\xF9\x2E\x64\xC8\x39",
+		.clen	= 16 + 9 + 16,
+	}, {
+		// "enc 13 plain",
+		.key	=
+		"\x00\x00\x00\x01" // Format
+		"\x00\x00\x00\x20" // Ke len
+		"\x00\x00\x00\x20" // Ki len
+		"\xcd\x8a\x10\xe2\x79\xda\xdd\xb6\x90\x1e\xc3\x0b\xdf\x98\x73\x25"
+		"\x0f\x6e\xfc\x6a\x77\x36\x7d\x74\xdc\x3e\xe7\xf7\x4b\xc7\x77\x4e" // Ke
+		"\x15\x2f\x8c\x9d\xc9\x85\x79\x6e\xb1\x94\xed\x14\xc5\x9e\xac\xdd"
+		"\x41\x8a\x33\x32\x36\xb7\x8f\xaf\xa7\xc7\x9b\x04\xe0\xac\xe7\xbf", // Ki
+		.klen	= 4 * 3 + 32 * 2,
+		.ptext	=
+		"\xCF\x9B\xCA\x6D\xF1\x14\x4E\x0C\x0A\xF9\xB8\xF3\x4C\x90\xD5\x14" // Confounder
+		"13 bytes byte",
+		.plen	= 16 + 13,
+		.ctext	=
+		"\xEE\xEC\x85\xA9\x81\x3C\xDC\x53\x67\x72\xAB\x9B\x42\xDE\xFC\x57"
+		"\x06\xF7\x26\xE9\x75\xDD\xE0\x5A\x87\xEB\x54\x06\xEA\x32\x4C\xA1"
+		"\x85\xC9\x98\x6B\x42\xAA\xBE\x79\x4B\x84\x82\x1B\xEE",
+		.clen	= 16 + 13 + 16,
+	}, {
+		// "enc 30 plain",
+		.key	=
+		"\x00\x00\x00\x01" // Format
+		"\x00\x00\x00\x20" // Ke len
+		"\x00\x00\x00\x20" // Ki len
+		"\x1d\x51\x47\xf3\x4b\xb0\x01\xa0\x4a\x68\xa7\x13\x46\xe7\x65\x4e"
+		"\x02\x23\xa6\x0d\x90\xbc\x2b\x79\xb4\xd8\x79\x56\xd4\x7c\xd4\x2a" // Ke
+		"\x04\x8d\xeb\xf7\xb1\x2c\x09\x32\xe8\xb2\x96\x99\x6c\x23\xf8\xb7"
+		"\x9d\x59\xb9\x7e\xa1\x19\xfc\x0c\x15\x6b\xf7\x88\xdc\x8c\x85\xe8", // Ki
+		.klen	= 4 * 3 + 32 * 2,
+		.ptext	=
+		"\x64\x4D\xEF\x38\xDA\x35\x00\x72\x75\x87\x8D\x21\x68\x55\xE2\x28" // Confounder
+		"30 bytes bytes bytes bytes byt", // Plain
+		.plen	= 16 + 30,
+		.ctext	=
+		"\x0E\x44\x68\x09\x85\x85\x5F\x2D\x1F\x18\x12\x52\x9C\xA8\x3B\xFD"
+		"\x8E\x34\x9D\xE6\xFD\x9A\xDA\x0B\xAA\xA0\x48\xD6\x8E\x26\x5F\xEB"
+		"\xF3\x4A\xD1\x25\x5A\x34\x49\x99\xAD\x37\x14\x68\x87\xA6\xC6\x84"
+		"\x57\x31\xAC\x7F\x46\x37\x6A\x05\x04\xCD\x06\x57\x14\x74",
+		.clen	= 16 + 30 + 16,
+	},
+};
+
 #endif	/* _CRYPTO_TESTMGR_H */



From dhowells at redhat.com  Fri Jan 17 10:35:14 2025
From: dhowells at redhat.com (David Howells)
Date: Fri, 17 Jan 2025 18:35:14 +0000
Subject: [RFC PATCH 05/24] crypto/krb5: Implement Kerberos crypto core
In-Reply-To: <20250117183538.881618-1-dhowells@redhat.com>
References: <20250117183538.881618-1-dhowells@redhat.com>
Message-ID: <20250117183538.881618-6-dhowells@redhat.com>

Provide core structures, an encoding-type registry and basic module and
config bits for a generic Kerberos crypto library.

Signed-off-by: David Howells <dhowells at redhat.com>
cc: Herbert Xu <herbert at gondor.apana.org.au>
cc: "David S. Miller" <davem at davemloft.net>
cc: Chuck Lever <chuck.lever at oracle.com>
cc: Marc Dionne <marc.dionne at auristor.com>
cc: Eric Dumazet <edumazet at google.com>
cc: Jakub Kicinski <kuba at kernel.org>
cc: Paolo Abeni <pabeni at redhat.com>
cc: Simon Horman <horms at kernel.org>
cc: linux-afs at lists.infradead.org
cc: linux-nfs at vger.kernel.org
cc: linux-crypto at vger.kernel.org
cc: netdev at vger.kernel.org
---
 crypto/Kconfig         |   1 +
 crypto/Makefile        |   2 +
 crypto/krb5/Kconfig    |  14 ++++++
 crypto/krb5/Makefile   |   9 ++++
 crypto/krb5/internal.h | 112 +++++++++++++++++++++++++++++++++++++++++
 crypto/krb5/krb5_api.c |  42 ++++++++++++++++
 include/crypto/krb5.h  |  54 ++++++++++++++++++++
 7 files changed, 234 insertions(+)
 create mode 100644 crypto/krb5/Kconfig
 create mode 100644 crypto/krb5/Makefile
 create mode 100644 crypto/krb5/internal.h
 create mode 100644 crypto/krb5/krb5_api.c

diff --git a/crypto/Kconfig b/crypto/Kconfig
index 18b1a3b3a258..d5d5d21c56ff 100644
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -1489,5 +1489,6 @@ endif
 source "drivers/crypto/Kconfig"
 source "crypto/asymmetric_keys/Kconfig"
 source "certs/Kconfig"
+source "crypto/krb5/Kconfig"
 
 endif	# if CRYPTO
diff --git a/crypto/Makefile b/crypto/Makefile
index eb40638c6c04..eb5ef38d47e0 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -214,3 +214,5 @@ obj-$(CONFIG_CRYPTO_SIMD) += crypto_simd.o
 # Key derivation function
 #
 obj-$(CONFIG_CRYPTO_KDF800108_CTR) += kdf_sp800108.o
+
+obj-$(CONFIG_CRYPTO_KRB5) += krb5/
diff --git a/crypto/krb5/Kconfig b/crypto/krb5/Kconfig
new file mode 100644
index 000000000000..079873618abf
--- /dev/null
+++ b/crypto/krb5/Kconfig
@@ -0,0 +1,14 @@
+config CRYPTO_KRB5
+	tristate "Kerberos 5 crypto"
+	select CRYPTO_MANAGER
+	select CRYPTO_KRB5ENC
+	select CRYPTO_AUTHENC
+	select CRYPTO_SKCIPHER
+	select CRYPTO_HASH_INFO
+	select CRYPTO_SHA1
+	select CRYPTO_CBC
+	select CRYPTO_CTS
+	select CRYPTO_AES
+	help
+	  Provide a library for provision of Kerberos-5-based crypto.  This is
+	  intended for network filesystems to use.
diff --git a/crypto/krb5/Makefile b/crypto/krb5/Makefile
new file mode 100644
index 000000000000..c450d0754772
--- /dev/null
+++ b/crypto/krb5/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for asymmetric cryptographic keys
+#
+
+krb5-y += \
+	krb5_api.o
+
+obj-$(CONFIG_CRYPTO_KRB5) += krb5.o
diff --git a/crypto/krb5/internal.h b/crypto/krb5/internal.h
new file mode 100644
index 000000000000..3ede858be4f7
--- /dev/null
+++ b/crypto/krb5/internal.h
@@ -0,0 +1,112 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* Kerberos5 crypto internals
+ *
+ * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells at redhat.com)
+ */
+
+#include <crypto/krb5.h>
+
+/*
+ * Profile used for key derivation and encryption.
+ */
+struct krb5_crypto_profile {
+	 /* Pseudo-random function */
+	int (*calc_PRF)(const struct krb5_enctype *krb5,
+			const struct krb5_buffer *protocol_key,
+			const struct krb5_buffer *octet_string,
+			struct krb5_buffer *result,
+			gfp_t gfp);
+
+	/* Checksum key derivation */
+	int (*calc_Kc)(const struct krb5_enctype *krb5,
+		       const struct krb5_buffer *TK,
+		       const struct krb5_buffer *usage_constant,
+		       struct krb5_buffer *Kc,
+		       gfp_t gfp);
+
+	/* Encryption key derivation */
+	int (*calc_Ke)(const struct krb5_enctype *krb5,
+		       const struct krb5_buffer *TK,
+		       const struct krb5_buffer *usage_constant,
+		       struct krb5_buffer *Ke,
+		       gfp_t gfp);
+
+	 /* Integrity key derivation */
+	int (*calc_Ki)(const struct krb5_enctype *krb5,
+		       const struct krb5_buffer *TK,
+		       const struct krb5_buffer *usage_constant,
+		       struct krb5_buffer *Ki,
+		       gfp_t gfp);
+
+	/* Derive the keys needed for an encryption AEAD object. */
+	int (*derive_encrypt_keys)(const struct krb5_enctype *krb5,
+				   const struct krb5_buffer *TK,
+				   unsigned int usage,
+				   struct krb5_buffer *setkey,
+				   gfp_t gfp);
+
+	/* Directly load the keys needed for an encryption AEAD object. */
+	int (*load_encrypt_keys)(const struct krb5_enctype *krb5,
+				 const struct krb5_buffer *Ke,
+				 const struct krb5_buffer *Ki,
+				 struct krb5_buffer *setkey,
+				 gfp_t gfp);
+
+	/* Derive the key needed for a checksum hash object. */
+	int (*derive_checksum_key)(const struct krb5_enctype *krb5,
+				   const struct krb5_buffer *TK,
+				   unsigned int usage,
+				   struct krb5_buffer *setkey,
+				   gfp_t gfp);
+
+	/* Directly load the keys needed for a checksum hash object. */
+	int (*load_checksum_key)(const struct krb5_enctype *krb5,
+				 const struct krb5_buffer *Kc,
+				 struct krb5_buffer *setkey,
+				 gfp_t gfp);
+
+	/* Encrypt data in-place, inserting confounder and checksum. */
+	ssize_t (*encrypt)(const struct krb5_enctype *krb5,
+			   struct crypto_aead *aead,
+			   struct scatterlist *sg, unsigned int nr_sg,
+			   size_t sg_len,
+			   size_t data_offset, size_t data_len,
+			   bool preconfounded);
+
+	/* Decrypt data in-place, removing confounder and checksum */
+	int (*decrypt)(const struct krb5_enctype *krb5,
+		       struct crypto_aead *aead,
+		       struct scatterlist *sg, unsigned int nr_sg,
+		       size_t *_offset, size_t *_len);
+
+	/* Generate a MIC on part of a packet, inserting the checksum */
+	ssize_t (*get_mic)(const struct krb5_enctype *krb5,
+			   struct crypto_shash *shash,
+			   const struct krb5_buffer *metadata,
+			   struct scatterlist *sg, unsigned int nr_sg,
+			   size_t sg_len,
+			   size_t data_offset, size_t data_len);
+
+	/* Verify the MIC on a piece of data, removing the checksum */
+	int (*verify_mic)(const struct krb5_enctype *krb5,
+			  struct crypto_shash *shash,
+			  const struct krb5_buffer *metadata,
+			  struct scatterlist *sg, unsigned int nr_sg,
+			  size_t *_offset, size_t *_len);
+};
+
+/*
+ * Crypto size/alignment rounding convenience macros.
+ */
+#define crypto_roundup(X) ((unsigned int)round_up((X), CRYPTO_MINALIGN))
+
+#define krb5_aead_size(TFM) \
+	crypto_roundup(sizeof(struct aead_request) + crypto_aead_reqsize(TFM))
+#define krb5_aead_ivsize(TFM) \
+	crypto_roundup(crypto_aead_ivsize(TFM))
+#define krb5_shash_size(TFM) \
+	crypto_roundup(sizeof(struct shash_desc) + crypto_shash_descsize(TFM))
+#define krb5_digest_size(TFM) \
+	crypto_roundup(crypto_shash_digestsize(TFM))
+#define round16(x) (((x) + 15) & ~15)
diff --git a/crypto/krb5/krb5_api.c b/crypto/krb5/krb5_api.c
new file mode 100644
index 000000000000..5c1cd5d07fc3
--- /dev/null
+++ b/crypto/krb5/krb5_api.c
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Kerberos 5 crypto library.
+ *
+ * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells at redhat.com)
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include "internal.h"
+
+MODULE_DESCRIPTION("Kerberos 5 crypto");
+MODULE_AUTHOR("Red Hat, Inc.");
+MODULE_LICENSE("GPL");
+
+static const struct krb5_enctype *const krb5_supported_enctypes[] = {
+};
+
+/**
+ * crypto_krb5_find_enctype - Find the handler for a Kerberos5 encryption type
+ * @enctype: The standard Kerberos encryption type number
+ *
+ * Look up a Kerberos encryption type by number.  If successful, returns a
+ * pointer to the type tables; returns NULL otherwise.
+ */
+const struct krb5_enctype *crypto_krb5_find_enctype(u32 enctype)
+{
+	const struct krb5_enctype *krb5;
+	size_t i;
+
+	for (i = 0; i < ARRAY_SIZE(krb5_supported_enctypes); i++) {
+		krb5 = krb5_supported_enctypes[i];
+		if (krb5->etype == enctype)
+			return krb5;
+	}
+
+	return NULL;
+}
+EXPORT_SYMBOL(crypto_krb5_find_enctype);
diff --git a/include/crypto/krb5.h b/include/crypto/krb5.h
index 8949a9b71de3..a67a5e1a0a4f 100644
--- a/include/crypto/krb5.h
+++ b/include/crypto/krb5.h
@@ -8,6 +8,12 @@
 #ifndef _CRYPTO_KRB5_H
 #define _CRYPTO_KRB5_H
 
+#include <linux/crypto.h>
+#include <crypto/aead.h>
+
+struct crypto_shash;
+struct scatterlist;
+
 /*
  * Per Kerberos v5 protocol spec crypto types from the wire.  These get mapped
  * to linux kernel crypto routines.
@@ -48,6 +54,54 @@
 #define KEY_USAGE_SEED_ENCRYPTION       (0xAA)
 #define KEY_USAGE_SEED_INTEGRITY        (0x55)
 
+/*
+ * Mode of operation.
+ */
+enum krb5_crypto_mode {
+	KRB5_CHECKSUM_MODE,	/* Checksum only */
+	KRB5_ENCRYPT_MODE,	/* Fully encrypted, possibly with integrity checksum */
+};
+
+struct krb5_buffer {
+	unsigned int	len;
+	void		*data;
+};
+
+/*
+ * Kerberos encoding type definition.
+ */
+struct krb5_enctype {
+	int		etype;		/* Encryption (key) type */
+	int		ctype;		/* Checksum type */
+	const char	*name;		/* "Friendly" name */
+	const char	*encrypt_name;	/* Crypto encrypt+checksum name */
+	const char	*cksum_name;	/* Crypto checksum name */
+	const char	*hash_name;	/* Crypto hash name */
+	const char	*derivation_enc; /* Cipher used in key derivation */
+	u16		block_len;	/* Length of encryption block */
+	u16		conf_len;	/* Length of confounder (normally == block_len) */
+	u16		cksum_len;	/* Length of checksum */
+	u16		key_bytes;	/* Length of raw key, in bytes */
+	u16		key_len;	/* Length of final key, in bytes */
+	u16		hash_len;	/* Length of hash in bytes */
+	u16		prf_len;	/* Length of PRF() result in bytes */
+	u16		Kc_len;		/* Length of Kc in bytes */
+	u16		Ke_len;		/* Length of Ke in bytes */
+	u16		Ki_len;		/* Length of Ki in bytes */
+	bool		keyed_cksum;	/* T if a keyed cksum */
+
+	const struct krb5_crypto_profile *profile;
+
+	int (*random_to_key)(const struct krb5_enctype *krb5,
+			     const struct krb5_buffer *in,
+			     struct krb5_buffer *out);	/* complete key generation */
+};
+
+/*
+ * krb5_api.c
+ */
+const struct krb5_enctype *crypto_krb5_find_enctype(u32 enctype);
+
 /*
  * krb5enc.c
  */



From dhowells at redhat.com  Fri Jan 17 10:35:15 2025
From: dhowells at redhat.com (David Howells)
Date: Fri, 17 Jan 2025 18:35:15 +0000
Subject: [RFC PATCH 06/24] crypto/krb5: Add an API to query the layout of the crypto section
In-Reply-To: <20250117183538.881618-1-dhowells@redhat.com>
References: <20250117183538.881618-1-dhowells@redhat.com>
Message-ID: <20250117183538.881618-7-dhowells@redhat.com>

Provide some functions to allow the called to find out about the layout of
the crypto section:

 (1) Calculate, for a given size of data, how big a buffer will be
     required to hold it and where the data will be within it.

 (2) Calculate, for an amount of buffer, what's the maximum size of data
     that will fit therein, and where it will start.

 (3) Determine where the data will be in a received message.

Signed-off-by: David Howells <dhowells at redhat.com>
cc: Herbert Xu <herbert at gondor.apana.org.au>
cc: "David S. Miller" <davem at davemloft.net>
cc: Chuck Lever <chuck.lever at oracle.com>
cc: Marc Dionne <marc.dionne at auristor.com>
cc: Eric Dumazet <edumazet at google.com>
cc: Jakub Kicinski <kuba at kernel.org>
cc: Paolo Abeni <pabeni at redhat.com>
cc: Simon Horman <horms at kernel.org>
cc: linux-afs at lists.infradead.org
cc: linux-nfs at vger.kernel.org
cc: linux-crypto at vger.kernel.org
cc: netdev at vger.kernel.org
---
 crypto/krb5/krb5_api.c | 108 +++++++++++++++++++++++++++++++++++++++++
 include/crypto/krb5.h  |   9 ++++
 2 files changed, 117 insertions(+)

diff --git a/crypto/krb5/krb5_api.c b/crypto/krb5/krb5_api.c
index 5c1cd5d07fc3..f6d1bc813daa 100644
--- a/crypto/krb5/krb5_api.c
+++ b/crypto/krb5/krb5_api.c
@@ -40,3 +40,111 @@ const struct krb5_enctype *crypto_krb5_find_enctype(u32 enctype)
 	return NULL;
 }
 EXPORT_SYMBOL(crypto_krb5_find_enctype);
+
+/**
+ * crypto_krb5_how_much_buffer - Work out how much buffer is required for an amount of data
+ * @krb5: The encoding to use.
+ * @mode: The mode in which to operated (checksum/encrypt)
+ * @data_size: How much data we want to allow for
+ * @_offset: Where to place the offset into the buffer
+ *
+ * Calculate how much buffer space is required to wrap a given amount of data.
+ * This allows for a confounder, padding and checksum as appropriate.  The
+ * amount of buffer required is returned and the offset into the buffer at
+ * which the data will start is placed in *_offset.
+ */
+size_t crypto_krb5_how_much_buffer(const struct krb5_enctype *krb5,
+				   enum krb5_crypto_mode mode,
+				   size_t data_size, size_t *_offset)
+{
+	switch (mode) {
+	case KRB5_CHECKSUM_MODE:
+		*_offset = krb5->cksum_len;
+		return krb5->cksum_len + data_size;
+
+	case KRB5_ENCRYPT_MODE:
+		*_offset = krb5->conf_len;
+		return krb5->conf_len + data_size + krb5->cksum_len;
+
+	default:
+		WARN_ON(1);
+		*_offset = 0;
+		return 0;
+	}
+}
+EXPORT_SYMBOL(crypto_krb5_how_much_buffer);
+
+/**
+ * crypto_krb5_how_much_data - Work out how much data can fit in an amount of buffer
+ * @krb5: The encoding to use.
+ * @mode: The mode in which to operated (checksum/encrypt)
+ * @_buffer_size: How much buffer we want to allow for (may be reduced)
+ * @_offset: Where to place the offset into the buffer
+ *
+ * Calculate how much data can be fitted into given amount of buffer.  This
+ * allows for a confounder, padding and checksum as appropriate.  The amount of
+ * data that will fit is returned, the amount of buffer required is shrunk to
+ * allow for alignment and the offset into the buffer at which the data will
+ * start is placed in *_offset.
+ */
+size_t crypto_krb5_how_much_data(const struct krb5_enctype *krb5,
+				 enum krb5_crypto_mode mode,
+				 size_t *_buffer_size, size_t *_offset)
+{
+	size_t buffer_size = *_buffer_size, data_size;
+
+	switch (mode) {
+	case KRB5_CHECKSUM_MODE:
+		if (WARN_ON(buffer_size < krb5->cksum_len + 1))
+			goto bad;
+		*_offset = krb5->cksum_len;
+		return buffer_size - krb5->cksum_len;
+
+	case KRB5_ENCRYPT_MODE:
+		if (WARN_ON(buffer_size < krb5->conf_len + 1 + krb5->cksum_len))
+			goto bad;
+		data_size = buffer_size - krb5->cksum_len;
+		*_offset = krb5->conf_len;
+		return data_size - krb5->conf_len;
+
+	default:
+		WARN_ON(1);
+		goto bad;
+	}
+
+bad:
+	*_offset = 0;
+	return 0;
+}
+EXPORT_SYMBOL(crypto_krb5_how_much_data);
+
+/**
+ * crypto_krb5_where_is_the_data - Find the data in a decrypted message
+ * @krb5: The encoding to use.
+ * @mode: Mode of operation
+ * @_offset: Offset of the secure blob in the buffer; updated to data offset.
+ * @_len: The length of the secure blob; updated to data length.
+ *
+ * Find the offset and size of the data in a secure message so that this
+ * information can be used in the metadata buffer which will get added to the
+ * digest by crypto_krb5_verify_mic().
+ */
+void crypto_krb5_where_is_the_data(const struct krb5_enctype *krb5,
+				   enum krb5_crypto_mode mode,
+				   size_t *_offset, size_t *_len)
+{
+	switch (mode) {
+	case KRB5_CHECKSUM_MODE:
+		*_offset += krb5->cksum_len;
+		*_len -= krb5->cksum_len;
+		return;
+	case KRB5_ENCRYPT_MODE:
+		*_offset += krb5->conf_len;
+		*_len -= krb5->conf_len + krb5->cksum_len;
+		return;
+	default:
+		WARN_ON_ONCE(1);
+		return;
+	}
+}
+EXPORT_SYMBOL(crypto_krb5_where_is_the_data);
diff --git a/include/crypto/krb5.h b/include/crypto/krb5.h
index a67a5e1a0a4f..e7dfe0b7c60d 100644
--- a/include/crypto/krb5.h
+++ b/include/crypto/krb5.h
@@ -101,6 +101,15 @@ struct krb5_enctype {
  * krb5_api.c
  */
 const struct krb5_enctype *crypto_krb5_find_enctype(u32 enctype);
+size_t crypto_krb5_how_much_buffer(const struct krb5_enctype *krb5,
+				   enum krb5_crypto_mode mode,
+				   size_t data_size, size_t *_offset);
+size_t crypto_krb5_how_much_data(const struct krb5_enctype *krb5,
+				 enum krb5_crypto_mode mode,
+				 size_t *_buffer_size, size_t *_offset);
+void crypto_krb5_where_is_the_data(const struct krb5_enctype *krb5,
+				   enum krb5_crypto_mode mode,
+				   size_t *_offset, size_t *_len);
 
 /*
  * krb5enc.c



From dhowells at redhat.com  Fri Jan 17 10:35:16 2025
From: dhowells at redhat.com (David Howells)
Date: Fri, 17 Jan 2025 18:35:16 +0000
Subject: [RFC PATCH 07/24] crypto/krb5: Add an API to alloc and prepare a crypto object
In-Reply-To: <20250117183538.881618-1-dhowells@redhat.com>
References: <20250117183538.881618-1-dhowells@redhat.com>
Message-ID: <20250117183538.881618-8-dhowells@redhat.com>

Add an API by which users of the krb5 crypto library can get an allocated
and keyed crypto object.

For encryption-mode operation, an AEAD object is returned; for
checksum-mode operation, a synchronous hash object is returned.

Signed-off-by: David Howells <dhowells at redhat.com>
cc: Herbert Xu <herbert at gondor.apana.org.au>
cc: "David S. Miller" <davem at davemloft.net>
cc: Chuck Lever <chuck.lever at oracle.com>
cc: Marc Dionne <marc.dionne at auristor.com>
cc: Eric Dumazet <edumazet at google.com>
cc: Jakub Kicinski <kuba at kernel.org>
cc: Paolo Abeni <pabeni at redhat.com>
cc: Simon Horman <horms at kernel.org>
cc: linux-afs at lists.infradead.org
cc: linux-nfs at vger.kernel.org
cc: linux-crypto at vger.kernel.org
cc: netdev at vger.kernel.org
---
 crypto/krb5/internal.h |  10 +++
 crypto/krb5/krb5_api.c | 144 +++++++++++++++++++++++++++++++++++++++++
 include/crypto/krb5.h  |   7 ++
 3 files changed, 161 insertions(+)

diff --git a/crypto/krb5/internal.h b/crypto/krb5/internal.h
index 3ede858be4f7..b542d24e5fa5 100644
--- a/crypto/krb5/internal.h
+++ b/crypto/krb5/internal.h
@@ -110,3 +110,13 @@ struct krb5_crypto_profile {
 #define krb5_digest_size(TFM) \
 	crypto_roundup(crypto_shash_digestsize(TFM))
 #define round16(x) (((x) + 15) & ~15)
+
+/*
+ * krb5_api.c
+ */
+struct crypto_aead *krb5_prepare_encryption(const struct krb5_enctype *krb5,
+					    const struct krb5_buffer *keys,
+					    gfp_t gfp);
+struct crypto_shash *krb5_prepare_checksum(const struct krb5_enctype *krb5,
+					   const struct krb5_buffer *Kc,
+					   gfp_t gfp);
diff --git a/crypto/krb5/krb5_api.c b/crypto/krb5/krb5_api.c
index f6d1bc813daa..f7f2528b3895 100644
--- a/crypto/krb5/krb5_api.c
+++ b/crypto/krb5/krb5_api.c
@@ -148,3 +148,147 @@ void crypto_krb5_where_is_the_data(const struct krb5_enctype *krb5,
 	}
 }
 EXPORT_SYMBOL(crypto_krb5_where_is_the_data);
+
+/*
+ * Prepare the encryption with derived key data.
+ */
+struct crypto_aead *krb5_prepare_encryption(const struct krb5_enctype *krb5,
+					    const struct krb5_buffer *keys,
+					    gfp_t gfp)
+{
+	struct crypto_aead *ci = NULL;
+	int ret = -ENOMEM;
+
+	ci = crypto_alloc_aead(krb5->encrypt_name, 0, 0);
+	if (IS_ERR(ci)) {
+		ret = PTR_ERR(ci);
+		if (ret == -ENOENT)
+			ret = -ENOPKG;
+		goto err;
+	}
+
+	ret = crypto_aead_setkey(ci, keys->data, keys->len);
+	if (ret < 0) {
+		pr_err("Couldn't set AEAD key %s: %d\n", krb5->encrypt_name, ret);
+		goto err_ci;
+	}
+
+	ret = crypto_aead_setauthsize(ci, krb5->cksum_len);
+	if (ret < 0) {
+		pr_err("Couldn't set AEAD authsize %s: %d\n", krb5->encrypt_name, ret);
+		goto err_ci;
+	}
+
+	return ci;
+err_ci:
+	crypto_free_aead(ci);
+err:
+	return ERR_PTR(ret);
+}
+
+/**
+ * crypto_krb5_prepare_encryption - Prepare AEAD crypto object for encryption-mode
+ * @krb5: The encoding to use.
+ * @TK: The transport key to use.
+ * @usage: The usage constant for key derivation.
+ * @gfp: Allocation flags.
+ *
+ * Allocate a crypto object that does all the necessary crypto, key it and set
+ * its parameters and return the crypto handle to it.  This can then be used to
+ * dispatch encrypt and decrypt operations.
+ */
+struct crypto_aead *crypto_krb5_prepare_encryption(const struct krb5_enctype *krb5,
+						   const struct krb5_buffer *TK,
+						   u32 usage, gfp_t gfp)
+{
+	struct crypto_aead *ci = NULL;
+	struct krb5_buffer keys = {};
+	int ret;
+
+	ret = krb5->profile->derive_encrypt_keys(krb5, TK, usage, &keys, gfp);
+	if (ret < 0)
+		goto err;
+
+	ci = krb5_prepare_encryption(krb5, &keys, gfp);
+	if (IS_ERR(ci)) {
+		ret = PTR_ERR(ci);
+		goto err;
+	}
+
+	kfree(keys.data);
+	return ci;
+err:
+	kfree(keys.data);
+	return ERR_PTR(ret);
+}
+EXPORT_SYMBOL(crypto_krb5_prepare_encryption);
+
+/*
+ * Prepare the checksum with derived key data.
+ */
+struct crypto_shash *krb5_prepare_checksum(const struct krb5_enctype *krb5,
+					   const struct krb5_buffer *Kc,
+					   gfp_t gfp)
+{
+	struct crypto_shash *ci = NULL;
+	int ret = -ENOMEM;
+
+	ci = crypto_alloc_shash(krb5->cksum_name, 0, 0);
+	if (IS_ERR(ci)) {
+		ret = PTR_ERR(ci);
+		if (ret == -ENOENT)
+			ret = -ENOPKG;
+		goto err;
+	}
+
+	ret = crypto_shash_setkey(ci, Kc->data, Kc->len);
+	if (ret < 0) {
+		pr_err("Couldn't set shash key %s: %d\n", krb5->cksum_name, ret);
+		goto err_ci;
+	}
+
+	return ci;
+err_ci:
+	crypto_free_shash(ci);
+err:
+	return ERR_PTR(ret);
+}
+
+/**
+ * crypto_krb5_prepare_checksum - Prepare AEAD crypto object for checksum-mode
+ * @krb5: The encoding to use.
+ * @TK: The transport key to use.
+ * @usage: The usage constant for key derivation.
+ * @gfp: Allocation flags.
+ *
+ * Allocate a crypto object that does all the necessary crypto, key it and set
+ * its parameters and return the crypto handle to it.  This can then be used to
+ * dispatch get_mic and verify_mic operations.
+ */
+struct crypto_shash *crypto_krb5_prepare_checksum(const struct krb5_enctype *krb5,
+						  const struct krb5_buffer *TK,
+						  u32 usage, gfp_t gfp)
+{
+	struct crypto_shash *ci = NULL;
+	struct krb5_buffer keys = {};
+	int ret;
+
+	ret = krb5->profile->derive_checksum_key(krb5, TK, usage, &keys, gfp);
+	if (ret < 0) {
+		pr_err("get_Kc failed %d\n", ret);
+		goto err;
+	}
+
+	ci = krb5_prepare_checksum(krb5, &keys, gfp);
+	if (IS_ERR(ci)) {
+		ret = PTR_ERR(ci);
+		goto err;
+	}
+
+	kfree(keys.data);
+	return ci;
+err:
+	kfree(keys.data);
+	return ERR_PTR(ret);
+}
+EXPORT_SYMBOL(crypto_krb5_prepare_checksum);
diff --git a/include/crypto/krb5.h b/include/crypto/krb5.h
index e7dfe0b7c60d..5ff052e2c157 100644
--- a/include/crypto/krb5.h
+++ b/include/crypto/krb5.h
@@ -10,6 +10,7 @@
 
 #include <linux/crypto.h>
 #include <crypto/aead.h>
+#include <crypto/hash.h>
 
 struct crypto_shash;
 struct scatterlist;
@@ -110,6 +111,12 @@ size_t crypto_krb5_how_much_data(const struct krb5_enctype *krb5,
 void crypto_krb5_where_is_the_data(const struct krb5_enctype *krb5,
 				   enum krb5_crypto_mode mode,
 				   size_t *_offset, size_t *_len);
+struct crypto_aead *crypto_krb5_prepare_encryption(const struct krb5_enctype *krb5,
+						   const struct krb5_buffer *TK,
+						   u32 usage, gfp_t gfp);
+struct crypto_shash *crypto_krb5_prepare_checksum(const struct krb5_enctype *krb5,
+						  const struct krb5_buffer *TK,
+						  u32 usage, gfp_t gfp);
 
 /*
  * krb5enc.c



From dhowells at redhat.com  Fri Jan 17 10:35:17 2025
From: dhowells at redhat.com (David Howells)
Date: Fri, 17 Jan 2025 18:35:17 +0000
Subject: [RFC PATCH 08/24] crypto/krb5: Add an API to perform requests
In-Reply-To: <20250117183538.881618-1-dhowells@redhat.com>
References: <20250117183538.881618-1-dhowells@redhat.com>
Message-ID: <20250117183538.881618-9-dhowells@redhat.com>

Add an API by which users of the krb5 crypto library can perform crypto
requests, such as encrypt, decrypt, get_mic and verify_mic.  These
functions take the previously prepared crypto objects to work on.

Signed-off-by: David Howells <dhowells at redhat.com>
cc: Herbert Xu <herbert at gondor.apana.org.au>
cc: "David S. Miller" <davem at davemloft.net>
cc: Chuck Lever <chuck.lever at oracle.com>
cc: Marc Dionne <marc.dionne at auristor.com>
cc: Eric Dumazet <edumazet at google.com>
cc: Jakub Kicinski <kuba at kernel.org>
cc: Paolo Abeni <pabeni at redhat.com>
cc: Simon Horman <horms at kernel.org>
cc: linux-afs at lists.infradead.org
cc: linux-nfs at vger.kernel.org
cc: linux-crypto at vger.kernel.org
cc: netdev at vger.kernel.org
---
 crypto/krb5/krb5_api.c | 141 +++++++++++++++++++++++++++++++++++++++++
 include/crypto/krb5.h  |  21 ++++++
 2 files changed, 162 insertions(+)

diff --git a/crypto/krb5/krb5_api.c b/crypto/krb5/krb5_api.c
index f7f2528b3895..8fc3a1b9d4ad 100644
--- a/crypto/krb5/krb5_api.c
+++ b/crypto/krb5/krb5_api.c
@@ -292,3 +292,144 @@ struct crypto_shash *crypto_krb5_prepare_checksum(const struct krb5_enctype *krb
 	return ERR_PTR(ret);
 }
 EXPORT_SYMBOL(crypto_krb5_prepare_checksum);
+
+/**
+ * crypto_krb5_encrypt - Apply Kerberos encryption and integrity.
+ * @krb5: The encoding to use.
+ * @aead: The keyed crypto object to use.
+ * @sg: Scatterlist defining the crypto buffer.
+ * @nr_sg: The number of elements in @sg.
+ * @sg_len: The size of the buffer.
+ * @data_offset: The offset of the data in the @sg buffer.
+ * @data_len: The length of the data.
+ * @preconfounded: True if the confounder is already inserted.
+ *
+ * Using the specified Kerberos encoding, insert a confounder and padding as
+ * needed, encrypt this and the data in place and insert an integrity checksum
+ * into the buffer.
+ *
+ * The buffer must include space for the confounder, the checksum and any
+ * padding required.  The caller can preinsert the confounder into the buffer
+ * (for testing, for example).
+ *
+ * The resulting secured blob may be less than the size of the buffer.
+ *
+ * Returns the size of the secure blob if successful, -ENOMEM on an allocation
+ * failure, -EFAULT if there is insufficient space, -EMSGSIZE if the confounder
+ * is too short or the data is misaligned.  Other errors may also be returned
+ * from the crypto layer.
+ */
+ssize_t crypto_krb5_encrypt(const struct krb5_enctype *krb5,
+			    struct crypto_aead *aead,
+			    struct scatterlist *sg, unsigned int nr_sg,
+			    size_t sg_len,
+			    size_t data_offset, size_t data_len,
+			    bool preconfounded)
+{
+	if (WARN_ON(data_offset > sg_len ||
+		    data_len > sg_len ||
+		    data_offset > sg_len - data_len))
+		return -EMSGSIZE;
+	return krb5->profile->encrypt(krb5, aead, sg, nr_sg, sg_len,
+				      data_offset, data_len, preconfounded);
+}
+EXPORT_SYMBOL(crypto_krb5_encrypt);
+
+/**
+ * crypto_krb5_decrypt - Validate and remove Kerberos encryption and integrity.
+ * @krb5: The encoding to use.
+ * @aead: The keyed crypto object to use.
+ * @sg: Scatterlist defining the crypto buffer.
+ * @nr_sg: The number of elements in @sg.
+ * @_offset: Offset of the secure blob in the buffer; updated to data offset.
+ * @_len: The length of the secure blob; updated to data length.
+ *
+ * Using the specified Kerberos encoding, check and remove the integrity
+ * checksum and decrypt the secure region, stripping off the confounder.
+ *
+ * If successful, @_offset and @_len are updated to outline the region in which
+ * the data plus the trailing padding are stored.  The caller is responsible
+ * for working out how much padding there is and removing it.
+ *
+ * Returns the 0 if successful, -ENOMEM on an allocation failure; sets
+ * *_error_code and returns -EPROTO if the data cannot be parsed, or -EBADMSG
+ * if the integrity checksum doesn't match).  Other errors may also be returned
+ * from the crypto layer.
+ */
+int crypto_krb5_decrypt(const struct krb5_enctype *krb5,
+			struct crypto_aead *aead,
+			struct scatterlist *sg, unsigned int nr_sg,
+			size_t *_offset, size_t *_len)
+{
+	return krb5->profile->decrypt(krb5, aead, sg, nr_sg, _offset, _len);
+}
+EXPORT_SYMBOL(crypto_krb5_decrypt);
+
+/**
+ * crypto_krb5_get_mic - Apply Kerberos integrity checksum.
+ * @krb5: The encoding to use.
+ * @shash: The keyed hash to use.
+ * @metadata: Metadata to add into the hash before adding the data.
+ * @sg: Scatterlist defining the crypto buffer.
+ * @nr_sg: The number of elements in @sg.
+ * @sg_len: The size of the buffer.
+ * @data_offset: The offset of the data in the @sg buffer.
+ * @data_len: The length of the data.
+ *
+ * Using the specified Kerberos encoding, calculate and insert an integrity
+ * checksum into the buffer.
+ *
+ * The buffer must include space for the checksum at the front.
+ *
+ * Returns the size of the secure blob if successful, -ENOMEM on an allocation
+ * failure, -EFAULT if there is insufficient space, -EMSGSIZE if the gap for
+ * the checksum is too short.  Other errors may also be returned from the
+ * crypto layer.
+ */
+ssize_t crypto_krb5_get_mic(const struct krb5_enctype *krb5,
+			    struct crypto_shash *shash,
+			    const struct krb5_buffer *metadata,
+			    struct scatterlist *sg, unsigned int nr_sg,
+			    size_t sg_len,
+			    size_t data_offset, size_t data_len)
+{
+	if (WARN_ON(data_offset > sg_len ||
+		    data_len > sg_len ||
+		    data_offset > sg_len - data_len))
+		return -EMSGSIZE;
+	return krb5->profile->get_mic(krb5, shash, metadata, sg, nr_sg, sg_len,
+				      data_offset, data_len);
+}
+EXPORT_SYMBOL(crypto_krb5_get_mic);
+
+/**
+ * crypto_krb5_verify_mic - Validate and remove Kerberos integrity checksum.
+ * @krb5: The encoding to use.
+ * @shash: The keyed hash to use.
+ * @metadata: Metadata to add into the hash before adding the data.
+ * @sg: Scatterlist defining the crypto buffer.
+ * @nr_sg: The number of elements in @sg.
+ * @_offset: Offset of the secure blob in the buffer; updated to data offset.
+ * @_len: The length of the secure blob; updated to data length.
+ *
+ * Using the specified Kerberos encoding, check and remove the integrity
+ * checksum.
+ *
+ * If successful, @_offset and @_len are updated to outline the region in which
+ * the data is stored.
+ *
+ * Returns the 0 if successful, -ENOMEM on an allocation failure; sets
+ * *_error_code and returns -EPROTO if the data cannot be parsed, or -EBADMSG
+ * if the checksum doesn't match).  Other errors may also be returned from the
+ * crypto layer.
+ */
+int crypto_krb5_verify_mic(const struct krb5_enctype *krb5,
+			   struct crypto_shash *shash,
+			   const struct krb5_buffer *metadata,
+			   struct scatterlist *sg, unsigned int nr_sg,
+			   size_t *_offset, size_t *_len)
+{
+	return krb5->profile->verify_mic(krb5, shash, metadata, sg, nr_sg,
+					 _offset, _len);
+}
+EXPORT_SYMBOL(crypto_krb5_verify_mic);
diff --git a/include/crypto/krb5.h b/include/crypto/krb5.h
index 5ff052e2c157..4919e135b2ca 100644
--- a/include/crypto/krb5.h
+++ b/include/crypto/krb5.h
@@ -117,6 +117,27 @@ struct crypto_aead *crypto_krb5_prepare_encryption(const struct krb5_enctype *kr
 struct crypto_shash *crypto_krb5_prepare_checksum(const struct krb5_enctype *krb5,
 						  const struct krb5_buffer *TK,
 						  u32 usage, gfp_t gfp);
+ssize_t crypto_krb5_encrypt(const struct krb5_enctype *krb5,
+			    struct crypto_aead *aead,
+			    struct scatterlist *sg, unsigned int nr_sg,
+			    size_t sg_len,
+			    size_t data_offset, size_t data_len,
+			    bool preconfounded);
+int crypto_krb5_decrypt(const struct krb5_enctype *krb5,
+			struct crypto_aead *aead,
+			struct scatterlist *sg, unsigned int nr_sg,
+			size_t *_offset, size_t *_len);
+ssize_t crypto_krb5_get_mic(const struct krb5_enctype *krb5,
+			    struct crypto_shash *shash,
+			    const struct krb5_buffer *metadata,
+			    struct scatterlist *sg, unsigned int nr_sg,
+			    size_t sg_len,
+			    size_t data_offset, size_t data_len);
+int crypto_krb5_verify_mic(const struct krb5_enctype *krb5,
+			   struct crypto_shash *shash,
+			   const struct krb5_buffer *metadata,
+			   struct scatterlist *sg, unsigned int nr_sg,
+			   size_t *_offset, size_t *_len);
 
 /*
  * krb5enc.c



From dhowells at redhat.com  Fri Jan 17 10:35:18 2025
From: dhowells at redhat.com (David Howells)
Date: Fri, 17 Jan 2025 18:35:18 +0000
Subject: [RFC PATCH 09/24] crypto/krb5: Provide infrastructure and key derivation
In-Reply-To: <20250117183538.881618-1-dhowells@redhat.com>
References: <20250117183538.881618-1-dhowells@redhat.com>
Message-ID: <20250117183538.881618-10-dhowells@redhat.com>

Provide key derivation interface functions and a helper to implement the
PRF+ function from rfc4402.

Signed-off-by: David Howells <dhowells at redhat.com>
cc: Herbert Xu <herbert at gondor.apana.org.au>
cc: "David S. Miller" <davem at davemloft.net>
cc: Chuck Lever <chuck.lever at oracle.com>
cc: Marc Dionne <marc.dionne at auristor.com>
cc: Eric Dumazet <edumazet at google.com>
cc: Jakub Kicinski <kuba at kernel.org>
cc: Paolo Abeni <pabeni at redhat.com>
cc: Simon Horman <horms at kernel.org>
cc: linux-afs at lists.infradead.org
cc: linux-nfs at vger.kernel.org
cc: linux-crypto at vger.kernel.org
cc: netdev at vger.kernel.org
---
 crypto/krb5/Makefile   |   1 +
 crypto/krb5/internal.h |  10 +++
 crypto/krb5/krb5_kdf.c | 145 +++++++++++++++++++++++++++++++++++++++++
 include/crypto/krb5.h  |  10 +++
 4 files changed, 166 insertions(+)
 create mode 100644 crypto/krb5/krb5_kdf.c

diff --git a/crypto/krb5/Makefile b/crypto/krb5/Makefile
index c450d0754772..8c2050af8fed 100644
--- a/crypto/krb5/Makefile
+++ b/crypto/krb5/Makefile
@@ -4,6 +4,7 @@
 #
 
 krb5-y += \
+	krb5_kdf.o \
 	krb5_api.o
 
 obj-$(CONFIG_CRYPTO_KRB5) += krb5.o
diff --git a/crypto/krb5/internal.h b/crypto/krb5/internal.h
index b542d24e5fa5..50abda5169c7 100644
--- a/crypto/krb5/internal.h
+++ b/crypto/krb5/internal.h
@@ -120,3 +120,13 @@ struct crypto_aead *krb5_prepare_encryption(const struct krb5_enctype *krb5,
 struct crypto_shash *krb5_prepare_checksum(const struct krb5_enctype *krb5,
 					   const struct krb5_buffer *Kc,
 					   gfp_t gfp);
+
+/*
+ * krb5_kdf.c
+ */
+int krb5_derive_Kc(const struct krb5_enctype *krb5, const struct krb5_buffer *TK,
+		   u32 usage, struct krb5_buffer *key, gfp_t gfp);
+int krb5_derive_Ke(const struct krb5_enctype *krb5, const struct krb5_buffer *TK,
+		   u32 usage, struct krb5_buffer *key, gfp_t gfp);
+int krb5_derive_Ki(const struct krb5_enctype *krb5, const struct krb5_buffer *TK,
+		   u32 usage, struct krb5_buffer *key, gfp_t gfp);
diff --git a/crypto/krb5/krb5_kdf.c b/crypto/krb5/krb5_kdf.c
new file mode 100644
index 000000000000..6699e5469d1b
--- /dev/null
+++ b/crypto/krb5/krb5_kdf.c
@@ -0,0 +1,145 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Kerberos key derivation.
+ *
+ * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells at redhat.com)
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <crypto/skcipher.h>
+#include <crypto/hash.h>
+#include "internal.h"
+
+/**
+ * crypto_krb5_calc_PRFplus - Calculate PRF+ [RFC4402]
+ * @krb5: The encryption type to use
+ * @K: The protocol key for the pseudo-random function
+ * @L: The length of the output
+ * @S: The input octet string
+ * @result: Result buffer, sized to krb5->prf_len
+ * @gfp: Allocation restrictions
+ *
+ * Calculate the kerberos pseudo-random function, PRF+() by the following
+ * method:
+ *
+ *      PRF+(K, L, S) = truncate(L, T1 || T2 || .. || Tn)
+ *      Tn = PRF(K, n || S)
+ *      [rfc4402 sec 2]
+ */
+int crypto_krb5_calc_PRFplus(const struct krb5_enctype *krb5,
+			     const struct krb5_buffer *K,
+			     unsigned int L,
+			     const struct krb5_buffer *S,
+			     struct krb5_buffer *result,
+			     gfp_t gfp)
+{
+	struct krb5_buffer T_series, Tn, n_S;
+	void *buffer;
+	int ret, n = 1;
+
+	Tn.len = krb5->prf_len;
+	T_series.len = 0;
+	n_S.len = 4 + S->len;
+
+	buffer = kzalloc(round16(L + Tn.len) + round16(n_S.len), gfp);
+	if (!buffer)
+		return -ENOMEM;
+
+	T_series.data = buffer;
+	n_S.data = buffer + round16(L + Tn.len);
+	memcpy(n_S.data + 4, S->data, S->len);
+
+	while (T_series.len < L) {
+		*(__be32 *)(n_S.data) = htonl(n);
+		Tn.data = T_series.data + Tn.len * (n - 1);
+		ret = krb5->profile->calc_PRF(krb5, K, &n_S, &Tn, gfp);
+		if (ret < 0)
+			goto err;
+		T_series.len += Tn.len;
+		n++;
+	}
+
+	/* Truncate to L */
+	memcpy(result->data, T_series.data, L);
+	ret = 0;
+
+err:
+	kfree_sensitive(buffer);
+	return ret;
+}
+EXPORT_SYMBOL(crypto_krb5_calc_PRFplus);
+
+/**
+ * krb5_derive_Kc - Derive key Kc and install into a hash
+ * @krb5: The encryption type to use
+ * @TK: The base key
+ * @usage: The key usage number
+ * @key: Prepped buffer to store the key into
+ * @gfp: Allocation restrictions
+ *
+ * Derive the Kerberos Kc checksumming key.  The key is stored into the
+ * prepared buffer.
+ */
+int krb5_derive_Kc(const struct krb5_enctype *krb5, const struct krb5_buffer *TK,
+		   u32 usage, struct krb5_buffer *key, gfp_t gfp)
+{
+	u8 buf[5] __aligned(CRYPTO_MINALIGN);
+	struct krb5_buffer usage_constant = { .len = 5, .data = buf };
+
+	*(__be32 *)buf = cpu_to_be32(usage);
+	buf[4] = KEY_USAGE_SEED_CHECKSUM;
+
+	key->len = krb5->Kc_len;
+	return krb5->profile->calc_Kc(krb5, TK, &usage_constant, key, gfp);
+}
+
+/**
+ * krb5_derive_Ke - Derive key Ke and install into an skcipher
+ * @krb5: The encryption type to use
+ * @TK: The base key
+ * @usage: The key usage number
+ * @key: Prepped buffer to store the key into
+ * @gfp: Allocation restrictions
+ *
+ * Derive the Kerberos Ke encryption key.  The key is stored into the prepared
+ * buffer.
+ */
+int krb5_derive_Ke(const struct krb5_enctype *krb5, const struct krb5_buffer *TK,
+		   u32 usage, struct krb5_buffer *key, gfp_t gfp)
+{
+	u8 buf[5] __aligned(CRYPTO_MINALIGN);
+	struct krb5_buffer usage_constant = { .len = 5, .data = buf };
+
+	*(__be32 *)buf = cpu_to_be32(usage);
+	buf[4] = KEY_USAGE_SEED_ENCRYPTION;
+
+	key->len = krb5->Ke_len;
+	return krb5->profile->calc_Ke(krb5, TK, &usage_constant, key, gfp);
+}
+
+/**
+ * krb5_derive_Ki - Derive key Ki and install into a hash
+ * @krb5: The encryption type to use
+ * @TK: The base key
+ * @usage: The key usage number
+ * @key: Prepped buffer to store the key into
+ * @gfp: Allocation restrictions
+ *
+ * Derive the Kerberos Ki integrity checksum key.  The key is stored into the
+ * prepared buffer.
+ */
+int krb5_derive_Ki(const struct krb5_enctype *krb5, const struct krb5_buffer *TK,
+		   u32 usage, struct krb5_buffer *key, gfp_t gfp)
+{
+	u8 buf[5] __aligned(CRYPTO_MINALIGN);
+	struct krb5_buffer usage_constant = { .len = 5, .data = buf };
+
+	*(__be32 *)buf = cpu_to_be32(usage);
+	buf[4] = KEY_USAGE_SEED_INTEGRITY;
+
+	key->len = krb5->Ki_len;
+	return krb5->profile->calc_Ki(krb5, TK, &usage_constant, key, gfp);
+}
diff --git a/include/crypto/krb5.h b/include/crypto/krb5.h
index 4919e135b2ca..a24d9ee53675 100644
--- a/include/crypto/krb5.h
+++ b/include/crypto/krb5.h
@@ -139,6 +139,16 @@ int crypto_krb5_verify_mic(const struct krb5_enctype *krb5,
 			   struct scatterlist *sg, unsigned int nr_sg,
 			   size_t *_offset, size_t *_len);
 
+/*
+ * krb5_kdf.c
+ */
+int crypto_krb5_calc_PRFplus(const struct krb5_enctype *krb5,
+			     const struct krb5_buffer *K,
+			     unsigned int L,
+			     const struct krb5_buffer *S,
+			     struct krb5_buffer *result,
+			     gfp_t gfp);
+
 /*
  * krb5enc.c
  */



From dhowells at redhat.com  Fri Jan 17 10:35:19 2025
From: dhowells at redhat.com (David Howells)
Date: Fri, 17 Jan 2025 18:35:19 +0000
Subject: [RFC PATCH 10/24] crypto/krb5: Implement the Kerberos5 rfc3961 key derivation
In-Reply-To: <20250117183538.881618-1-dhowells@redhat.com>
References: <20250117183538.881618-1-dhowells@redhat.com>
Message-ID: <20250117183538.881618-11-dhowells@redhat.com>

Implement the simplified crypto profile for Kerberos 5 rfc3961 with the
pseudo-random function, PRF(), from section 5.3 and the key derivation
function, DK() from section 5.1.

Signed-off-by: David Howells <dhowells at redhat.com>
cc: Herbert Xu <herbert at gondor.apana.org.au>
cc: "David S. Miller" <davem at davemloft.net>
cc: Chuck Lever <chuck.lever at oracle.com>
cc: Marc Dionne <marc.dionne at auristor.com>
cc: Eric Dumazet <edumazet at google.com>
cc: Jakub Kicinski <kuba at kernel.org>
cc: Paolo Abeni <pabeni at redhat.com>
cc: Simon Horman <horms at kernel.org>
cc: linux-afs at lists.infradead.org
cc: linux-nfs at vger.kernel.org
cc: linux-crypto at vger.kernel.org
cc: netdev at vger.kernel.org
---
 crypto/krb5/Makefile             |   3 +-
 crypto/krb5/internal.h           |   6 +
 crypto/krb5/rfc3961_simplified.c | 407 +++++++++++++++++++++++++++++++
 3 files changed, 415 insertions(+), 1 deletion(-)
 create mode 100644 crypto/krb5/rfc3961_simplified.c

diff --git a/crypto/krb5/Makefile b/crypto/krb5/Makefile
index 8c2050af8fed..8dad8e3bf086 100644
--- a/crypto/krb5/Makefile
+++ b/crypto/krb5/Makefile
@@ -5,6 +5,7 @@
 
 krb5-y += \
 	krb5_kdf.o \
-	krb5_api.o
+	krb5_api.o \
+	rfc3961_simplified.o
 
 obj-$(CONFIG_CRYPTO_KRB5) += krb5.o
diff --git a/crypto/krb5/internal.h b/crypto/krb5/internal.h
index 50abda5169c7..7d60977dc0c5 100644
--- a/crypto/krb5/internal.h
+++ b/crypto/krb5/internal.h
@@ -5,6 +5,7 @@
  * Written by David Howells (dhowells at redhat.com)
  */
 
+#include <linux/scatterlist.h>
 #include <crypto/krb5.h>
 
 /*
@@ -130,3 +131,8 @@ int krb5_derive_Ke(const struct krb5_enctype *krb5, const struct krb5_buffer *TK
 		   u32 usage, struct krb5_buffer *key, gfp_t gfp);
 int krb5_derive_Ki(const struct krb5_enctype *krb5, const struct krb5_buffer *TK,
 		   u32 usage, struct krb5_buffer *key, gfp_t gfp);
+
+/*
+ * rfc3961_simplified.c
+ */
+extern const struct krb5_crypto_profile rfc3961_simplified_profile;
diff --git a/crypto/krb5/rfc3961_simplified.c b/crypto/krb5/rfc3961_simplified.c
new file mode 100644
index 000000000000..492ad85cdd56
--- /dev/null
+++ b/crypto/krb5/rfc3961_simplified.c
@@ -0,0 +1,407 @@
+// SPDX-License-Identifier: BSD-3-Clause
+/* rfc3961 Kerberos 5 simplified crypto profile.
+ *
+ * Parts borrowed from net/sunrpc/auth_gss/.
+ */
+/*
+ * COPYRIGHT (c) 2008
+ * The Regents of the University of Michigan
+ * ALL RIGHTS RESERVED
+ *
+ * Permission is granted to use, copy, create derivative works
+ * and redistribute this software and such derivative works
+ * for any purpose, so long as the name of The University of
+ * Michigan is not used in any advertising or publicity
+ * pertaining to the use of distribution of this software
+ * without specific, written prior authorization.  If the
+ * above copyright notice or any other identification of the
+ * University of Michigan is included in any copy of any
+ * portion of this software, then the disclaimer below must
+ * also be included.
+ *
+ * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION
+ * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY
+ * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF
+ * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
+ * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE
+ * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
+ * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGES.
+ */
+
+/*
+ * Copyright (C) 1998 by the FundsXpress, INC.
+ *
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may require
+ * a specific license from the United States Government.  It is the
+ * responsibility of any person or organization contemplating export to
+ * obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of FundsXpress. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  FundsXpress makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/*
+ * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells at redhat.com)
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/slab.h>
+#include <linux/lcm.h>
+#include <crypto/skcipher.h>
+#include <crypto/hash.h>
+#include "internal.h"
+
+/* Maximum blocksize for the supported crypto algorithms */
+#define KRB5_MAX_BLOCKSIZE  (16)
+
+static int rfc3961_do_encrypt(struct crypto_sync_skcipher *tfm, void *iv,
+			      const struct krb5_buffer *in, struct krb5_buffer *out)
+{
+	struct scatterlist sg[1];
+	u8 local_iv[KRB5_MAX_BLOCKSIZE] __aligned(KRB5_MAX_BLOCKSIZE) = {0};
+	SYNC_SKCIPHER_REQUEST_ON_STACK(req, tfm);
+	int ret;
+
+	if (WARN_ON(in->len != out->len))
+		return -EINVAL;
+	if (out->len % crypto_sync_skcipher_blocksize(tfm) != 0)
+		return -EINVAL;
+
+	if (crypto_sync_skcipher_ivsize(tfm) > KRB5_MAX_BLOCKSIZE)
+		return -EINVAL;
+
+	if (iv)
+		memcpy(local_iv, iv, crypto_sync_skcipher_ivsize(tfm));
+
+	memcpy(out->data, in->data, out->len);
+	sg_init_one(sg, out->data, out->len);
+
+	skcipher_request_set_sync_tfm(req, tfm);
+	skcipher_request_set_callback(req, 0, NULL, NULL);
+	skcipher_request_set_crypt(req, sg, sg, out->len, local_iv);
+
+	ret = crypto_skcipher_encrypt(req);
+	skcipher_request_zero(req);
+	return ret;
+}
+
+/*
+ * Calculate an unkeyed basic hash.
+ */
+static int rfc3961_calc_H(const struct krb5_enctype *krb5,
+			  const struct krb5_buffer *data,
+			  struct krb5_buffer *digest,
+			  gfp_t gfp)
+{
+	struct crypto_shash *tfm;
+	struct shash_desc *desc;
+	size_t desc_size;
+	int ret = -ENOMEM;
+
+	tfm = crypto_alloc_shash(krb5->hash_name, 0, 0);
+	if (IS_ERR(tfm))
+		return (PTR_ERR(tfm) == -ENOENT) ? -ENOPKG : PTR_ERR(tfm);
+
+	desc_size = crypto_shash_descsize(tfm) + sizeof(*desc);
+
+	desc = kzalloc(desc_size, gfp);
+	if (!desc)
+		goto error_tfm;
+
+	digest->len = crypto_shash_digestsize(tfm);
+	digest->data = kzalloc(digest->len, gfp);
+	if (!digest->data)
+		goto error_desc;
+
+	desc->tfm = tfm;
+	ret = crypto_shash_init(desc);
+	if (ret < 0)
+		goto error_digest;
+
+	ret = crypto_shash_finup(desc, data->data, data->len, digest->data);
+	if (ret < 0)
+		goto error_digest;
+
+	goto error_desc;
+
+error_digest:
+	kfree_sensitive(digest->data);
+error_desc:
+	kfree_sensitive(desc);
+error_tfm:
+	crypto_free_shash(tfm);
+	return ret;
+}
+
+/*
+ * This is the n-fold function as described in rfc3961, sec 5.1
+ * Taken from MIT Kerberos and modified.
+ */
+static void rfc3961_nfold(const struct krb5_buffer *source, struct krb5_buffer *result)
+{
+	const u8 *in = source->data;
+	u8 *out = result->data;
+	unsigned long ulcm;
+	unsigned int inbits, outbits;
+	int byte, i, msbit;
+
+	/* the code below is more readable if I make these bytes instead of bits */
+	inbits = source->len;
+	outbits = result->len;
+
+	/* first compute lcm(n,k) */
+	ulcm = lcm(inbits, outbits);
+
+	/* now do the real work */
+	memset(out, 0, outbits);
+	byte = 0;
+
+	/* this will end up cycling through k lcm(k,n)/k times, which
+	 * is correct.
+	 */
+	for (i = ulcm-1; i >= 0; i--) {
+		/* compute the msbit in k which gets added into this byte */
+		msbit = (
+			/* first, start with the msbit in the first,
+			 * unrotated byte
+			 */
+			((inbits << 3) - 1) +
+			/* then, for each byte, shift to the right
+			 * for each repetition
+			 */
+			(((inbits << 3) + 13) * (i/inbits)) +
+			/* last, pick out the correct byte within
+			 * that shifted repetition
+			 */
+			((inbits - (i % inbits)) << 3)
+			 ) % (inbits << 3);
+
+		/* pull out the byte value itself */
+		byte += (((in[((inbits - 1) - (msbit >> 3)) % inbits] << 8) |
+			  (in[((inbits)     - (msbit >> 3)) % inbits]))
+			 >> ((msbit & 7) + 1)) & 0xff;
+
+		/* do the addition */
+		byte += out[i % outbits];
+		out[i % outbits] = byte & 0xff;
+
+		/* keep around the carry bit, if any */
+		byte >>= 8;
+	}
+
+	/* if there's a carry bit left over, add it back in */
+	if (byte) {
+		for (i = outbits - 1; i >= 0; i--) {
+			/* do the addition */
+			byte += out[i];
+			out[i] = byte & 0xff;
+
+			/* keep around the carry bit, if any */
+			byte >>= 8;
+		}
+	}
+}
+
+/*
+ * Calculate a derived key, DK(Base Key, Well-Known Constant)
+ *
+ * DK(Key, Constant) = random-to-key(DR(Key, Constant))
+ * DR(Key, Constant) = k-truncate(E(Key, Constant, initial-cipher-state))
+ * K1 = E(Key, n-fold(Constant), initial-cipher-state)
+ * K2 = E(Key, K1, initial-cipher-state)
+ * K3 = E(Key, K2, initial-cipher-state)
+ * K4 = ...
+ * DR(Key, Constant) = k-truncate(K1 | K2 | K3 | K4 ...)
+ * [rfc3961 sec 5.1]
+ */
+static int rfc3961_calc_DK(const struct krb5_enctype *krb5,
+			   const struct krb5_buffer *inkey,
+			   const struct krb5_buffer *in_constant,
+			   struct krb5_buffer *result,
+			   gfp_t gfp)
+{
+	unsigned int blocksize, keybytes, keylength, n;
+	struct krb5_buffer inblock, outblock, rawkey;
+	struct crypto_sync_skcipher *cipher;
+	int ret = -EINVAL;
+
+	blocksize = krb5->block_len;
+	keybytes = krb5->key_bytes;
+	keylength = krb5->key_len;
+
+	if (inkey->len != keylength || result->len != keylength)
+		return -EINVAL;
+	if (!krb5->random_to_key && result->len != keybytes)
+		ret = -EINVAL;
+
+	cipher = crypto_alloc_sync_skcipher(krb5->derivation_enc, 0, 0);
+	if (IS_ERR(cipher)) {
+		ret = (PTR_ERR(cipher) == -ENOENT) ? -ENOPKG : PTR_ERR(cipher);
+		goto err_return;
+	}
+	ret = crypto_sync_skcipher_setkey(cipher, inkey->data, inkey->len);
+	if (ret < 0)
+		goto err_free_cipher;
+
+	ret = -ENOMEM;
+	inblock.data = kzalloc(blocksize * 2 + keybytes, gfp);
+	if (!inblock.data)
+		goto err_free_cipher;
+
+	inblock.len	= blocksize;
+	outblock.data	= inblock.data + blocksize;
+	outblock.len	= blocksize;
+	rawkey.data	= outblock.data + blocksize;
+	rawkey.len	= keybytes;
+
+	/* initialize the input block */
+
+	if (in_constant->len == inblock.len)
+		memcpy(inblock.data, in_constant->data, inblock.len);
+	else
+		rfc3961_nfold(in_constant, &inblock);
+
+	/* loop encrypting the blocks until enough key bytes are generated */
+	n = 0;
+	while (n < rawkey.len) {
+		rfc3961_do_encrypt(cipher, NULL, &inblock, &outblock);
+
+		if (keybytes - n <= outblock.len) {
+			memcpy(rawkey.data + n, outblock.data, keybytes - n);
+			break;
+		}
+
+		memcpy(rawkey.data + n, outblock.data, outblock.len);
+		memcpy(inblock.data, outblock.data, outblock.len);
+		n += outblock.len;
+	}
+
+	/* postprocess the key */
+	if (!krb5->random_to_key) {
+		/* Identity random-to-key function. */
+		memcpy(result->data, rawkey.data, rawkey.len);
+		ret = 0;
+	} else {
+		ret = krb5->random_to_key(krb5, &rawkey, result);
+	}
+
+	kfree_sensitive(inblock.data);
+err_free_cipher:
+	crypto_free_sync_skcipher(cipher);
+err_return:
+	return ret;
+}
+
+/*
+ * Calculate single encryption, E()
+ *
+ *	E(Key, octets)
+ */
+static int rfc3961_calc_E(const struct krb5_enctype *krb5,
+			  const struct krb5_buffer *key,
+			  const struct krb5_buffer *in_data,
+			  struct krb5_buffer *result,
+			  gfp_t gfp)
+{
+	struct crypto_sync_skcipher *cipher;
+	int ret;
+
+	cipher = crypto_alloc_sync_skcipher(krb5->derivation_enc, 0, 0);
+	if (IS_ERR(cipher)) {
+		ret = (PTR_ERR(cipher) == -ENOENT) ? -ENOPKG : PTR_ERR(cipher);
+		goto err;
+	}
+
+	ret = crypto_sync_skcipher_setkey(cipher, key->data, key->len);
+	if (ret < 0)
+		goto err_free;
+
+	ret = rfc3961_do_encrypt(cipher, NULL, in_data, result);
+
+err_free:
+	crypto_free_sync_skcipher(cipher);
+err:
+	return ret;
+}
+
+/*
+ * Calculate the pseudo-random function, PRF().
+ *
+ *      tmp1 = H(octet-string)
+ *      tmp2 = truncate tmp1 to multiple of m
+ *      PRF = E(DK(protocol-key, prfconstant), tmp2, initial-cipher-state)
+ *
+ *      The "prfconstant" used in the PRF operation is the three-octet string
+ *      "prf".
+ *      [rfc3961 sec 5.3]
+ */
+static int rfc3961_calc_PRF(const struct krb5_enctype *krb5,
+			    const struct krb5_buffer *protocol_key,
+			    const struct krb5_buffer *octet_string,
+			    struct krb5_buffer *result,
+			    gfp_t gfp)
+{
+	static const struct krb5_buffer prfconstant = { 3, "prf" };
+	struct krb5_buffer derived_key;
+	struct krb5_buffer tmp1, tmp2;
+	unsigned int m = krb5->block_len;
+	void *buffer;
+	int ret;
+
+	if (result->len != krb5->prf_len)
+		return -EINVAL;
+
+	tmp1.len = krb5->hash_len;
+	derived_key.len = krb5->key_bytes;
+	buffer = kzalloc(round16(tmp1.len) + round16(derived_key.len), gfp);
+	if (!buffer)
+		return -ENOMEM;
+
+	tmp1.data = buffer;
+	derived_key.data = buffer + round16(tmp1.len);
+
+	ret = rfc3961_calc_H(krb5, octet_string, &tmp1, gfp);
+	if (ret < 0)
+		goto err;
+
+	tmp2.len = tmp1.len & ~(m - 1);
+	tmp2.data = tmp1.data;
+
+	ret = rfc3961_calc_DK(krb5, protocol_key, &prfconstant, &derived_key, gfp);
+	if (ret < 0)
+		goto err;
+
+	ret = rfc3961_calc_E(krb5, &derived_key, &tmp2, result, gfp);
+
+err:
+	kfree_sensitive(buffer);
+	return ret;
+}
+
+const struct krb5_crypto_profile rfc3961_simplified_profile = {
+	.calc_PRF	= rfc3961_calc_PRF,
+	.calc_Kc	= rfc3961_calc_DK,
+	.calc_Ke	= rfc3961_calc_DK,
+	.calc_Ki	= rfc3961_calc_DK,
+};



From dhowells at redhat.com  Fri Jan 17 10:35:20 2025
From: dhowells at redhat.com (David Howells)
Date: Fri, 17 Jan 2025 18:35:20 +0000
Subject: [RFC PATCH 11/24] crypto/krb5: Provide RFC3961 setkey packaging functions
In-Reply-To: <20250117183538.881618-1-dhowells@redhat.com>
References: <20250117183538.881618-1-dhowells@redhat.com>
Message-ID: <20250117183538.881618-12-dhowells@redhat.com>

Provide functions to derive keys according to RFC3961 (or load the derived
keys for the selftester where only derived keys are available) and to
package them up appropriately for passing to a krb5enc AEAD setkey or a
hash setkey function.

Signed-off-by: David Howells <dhowells at redhat.com>
cc: Herbert Xu <herbert at gondor.apana.org.au>
cc: "David S. Miller" <davem at davemloft.net>
cc: Chuck Lever <chuck.lever at oracle.com>
cc: Marc Dionne <marc.dionne at auristor.com>
cc: Eric Dumazet <edumazet at google.com>
cc: Jakub Kicinski <kuba at kernel.org>
cc: Paolo Abeni <pabeni at redhat.com>
cc: Simon Horman <horms at kernel.org>
cc: linux-afs at lists.infradead.org
cc: linux-nfs at vger.kernel.org
cc: linux-crypto at vger.kernel.org
cc: netdev at vger.kernel.org
---
 crypto/krb5/internal.h           |  20 ++++++
 crypto/krb5/rfc3961_simplified.c | 116 +++++++++++++++++++++++++++++--
 2 files changed, 132 insertions(+), 4 deletions(-)

diff --git a/crypto/krb5/internal.h b/crypto/krb5/internal.h
index 7d60977dc0c5..8418c23d5018 100644
--- a/crypto/krb5/internal.h
+++ b/crypto/krb5/internal.h
@@ -136,3 +136,23 @@ int krb5_derive_Ki(const struct krb5_enctype *krb5, const struct krb5_buffer *TK
  * rfc3961_simplified.c
  */
 extern const struct krb5_crypto_profile rfc3961_simplified_profile;
+
+int krb5enc_derive_encrypt_keys(const struct krb5_enctype *krb5,
+				const struct krb5_buffer *TK,
+				unsigned int usage,
+				struct krb5_buffer *setkey,
+				gfp_t gfp);
+int krb5enc_load_encrypt_keys(const struct krb5_enctype *krb5,
+			      const struct krb5_buffer *Ke,
+			      const struct krb5_buffer *Ki,
+			      struct krb5_buffer *setkey,
+			      gfp_t gfp);
+int rfc3961_derive_checksum_key(const struct krb5_enctype *krb5,
+				const struct krb5_buffer *TK,
+				unsigned int usage,
+				struct krb5_buffer *setkey,
+				gfp_t gfp);
+int rfc3961_load_checksum_key(const struct krb5_enctype *krb5,
+			      const struct krb5_buffer *Kc,
+			      struct krb5_buffer *setkey,
+			      gfp_t gfp);
diff --git a/crypto/krb5/rfc3961_simplified.c b/crypto/krb5/rfc3961_simplified.c
index 492ad85cdd56..d25fbd574dde 100644
--- a/crypto/krb5/rfc3961_simplified.c
+++ b/crypto/krb5/rfc3961_simplified.c
@@ -399,9 +399,117 @@ static int rfc3961_calc_PRF(const struct krb5_enctype *krb5,
 	return ret;
 }
 
+/*
+ * Derive the Ke and Ki keys and package them into a key parameter that can be
+ * given to the setkey of a krb5enc AEAD crypto object.
+ */
+int krb5enc_derive_encrypt_keys(const struct krb5_enctype *krb5,
+				const struct krb5_buffer *TK,
+				unsigned int usage,
+				struct krb5_buffer *setkey,
+				gfp_t gfp)
+{
+	struct krb5_buffer Ke, Ki;
+	__be32 *khdr = NULL;
+	int ret;
+
+	Ke.len  = krb5->Ke_len;
+	Ki.len  = krb5->Ki_len;
+	setkey->len = sizeof(__be32) * 3 + krb5->Ke_len + krb5->Ki_len;
+	setkey->data = kzalloc(setkey->len, GFP_KERNEL);
+	if (!setkey->data)
+		return -ENOMEM;
+
+	khdr = setkey->data;
+	Ke.data = setkey->data + 12;
+	Ki.data = setkey->data + 12 + Ke.len;
+
+	khdr[0] = htonl(1); /* Format 1 */
+	khdr[1] = htonl(Ke.len);
+	khdr[2] = htonl(Ki.len);
+
+	ret = krb5_derive_Ke(krb5, TK, usage, &Ke, gfp);
+	if (ret < 0) {
+		pr_err("get_Ke failed %d\n", ret);
+		return ret;
+	}
+	ret = krb5_derive_Ki(krb5, TK, usage, &Ki, gfp);
+	if (ret < 0)
+		pr_err("get_Ki failed %d\n", ret);
+	return ret;
+}
+
+/*
+ * Package predefined Ke and Ki keys and into a key parameter that can be given
+ * to the setkey of a krb5enc AEAD crypto object.
+ */
+int krb5enc_load_encrypt_keys(const struct krb5_enctype *krb5,
+			      const struct krb5_buffer *Ke,
+			      const struct krb5_buffer *Ki,
+			      struct krb5_buffer *setkey,
+			      gfp_t gfp)
+{
+	__be32 *khdr = NULL;
+
+	setkey->len = sizeof(__be32) * 3 + Ke->len + Ki->len;
+	setkey->data = kzalloc(setkey->len, GFP_KERNEL);
+	if (!setkey->data)
+		return -ENOMEM;
+
+	khdr = setkey->data;
+	khdr[0] = htonl(1); /* Format 1 */
+	khdr[1] = htonl(Ke->len);
+	khdr[2] = htonl(Ki->len);
+	memcpy(setkey->data + 12, Ke->data, Ke->len);
+	memcpy(setkey->data + 12 + Ke->len, Ki->data, Ki->len);
+	return 0;
+}
+
+/*
+ * Derive the Kc key for checksum-only mode and package it into a key parameter
+ * that can be given to the setkey of a hash crypto object.
+ */
+int rfc3961_derive_checksum_key(const struct krb5_enctype *krb5,
+				const struct krb5_buffer *TK,
+				unsigned int usage,
+				struct krb5_buffer *setkey,
+				gfp_t gfp)
+{
+	int ret;
+
+	setkey->len = krb5->Kc_len;
+	setkey->data = kzalloc(setkey->len, GFP_KERNEL);
+	if (!setkey->data)
+		return -ENOMEM;
+
+	ret = krb5_derive_Kc(krb5, TK, usage, setkey, gfp);
+	if (ret < 0)
+		pr_err("get_Kc failed %d\n", ret);
+	return ret;
+}
+
+/*
+ * Package a predefined Kc key for checksum-only mode into a key parameter that
+ * can be given to the setkey of a hash crypto object.
+ */
+int rfc3961_load_checksum_key(const struct krb5_enctype *krb5,
+			      const struct krb5_buffer *Kc,
+			      struct krb5_buffer *setkey,
+			      gfp_t gfp)
+{
+	setkey->len = krb5->Kc_len;
+	setkey->data = kmemdup(Kc->data, Kc->len, GFP_KERNEL);
+	if (!setkey->data)
+		return -ENOMEM;
+	return 0;
+}
 const struct krb5_crypto_profile rfc3961_simplified_profile = {
-	.calc_PRF	= rfc3961_calc_PRF,
-	.calc_Kc	= rfc3961_calc_DK,
-	.calc_Ke	= rfc3961_calc_DK,
-	.calc_Ki	= rfc3961_calc_DK,
+	.calc_PRF		= rfc3961_calc_PRF,
+	.calc_Kc		= rfc3961_calc_DK,
+	.calc_Ke		= rfc3961_calc_DK,
+	.calc_Ki		= rfc3961_calc_DK,
+	.derive_encrypt_keys	= krb5enc_derive_encrypt_keys,
+	.load_encrypt_keys	= krb5enc_load_encrypt_keys,
+	.derive_checksum_key	= rfc3961_derive_checksum_key,
+	.load_checksum_key	= rfc3961_load_checksum_key,
 };



From dhowells at redhat.com  Fri Jan 17 10:35:21 2025
From: dhowells at redhat.com (David Howells)
Date: Fri, 17 Jan 2025 18:35:21 +0000
Subject: [RFC PATCH 12/24] crypto/krb5: Implement the Kerberos5 rfc3961 encrypt and decrypt functions
In-Reply-To: <20250117183538.881618-1-dhowells@redhat.com>
References: <20250117183538.881618-1-dhowells@redhat.com>
Message-ID: <20250117183538.881618-13-dhowells@redhat.com>

Add functions that encrypt and decrypt a message according to rfc3961 sec
5.3, using Ki to checksum the data to be secured and Ke to encrypt it
during the encryption phase, then decrypting with Ke and verifying the
checksum with Ki in the decryption phase.

Signed-off-by: David Howells <dhowells at redhat.com>
cc: Herbert Xu <herbert at gondor.apana.org.au>
cc: "David S. Miller" <davem at davemloft.net>
cc: Chuck Lever <chuck.lever at oracle.com>
cc: Marc Dionne <marc.dionne at auristor.com>
cc: Eric Dumazet <edumazet at google.com>
cc: Jakub Kicinski <kuba at kernel.org>
cc: Paolo Abeni <pabeni at redhat.com>
cc: Simon Horman <horms at kernel.org>
cc: linux-afs at lists.infradead.org
cc: linux-nfs at vger.kernel.org
cc: linux-crypto at vger.kernel.org
cc: netdev at vger.kernel.org
---
 crypto/krb5/internal.h           |  13 +++
 crypto/krb5/rfc3961_simplified.c | 146 +++++++++++++++++++++++++++++++
 2 files changed, 159 insertions(+)

diff --git a/crypto/krb5/internal.h b/crypto/krb5/internal.h
index 8418c23d5018..4533fb342953 100644
--- a/crypto/krb5/internal.h
+++ b/crypto/krb5/internal.h
@@ -7,6 +7,8 @@
 
 #include <linux/scatterlist.h>
 #include <crypto/krb5.h>
+#include <crypto/hash.h>
+#include <crypto/skcipher.h>
 
 /*
  * Profile used for key derivation and encryption.
@@ -137,6 +139,8 @@ int krb5_derive_Ki(const struct krb5_enctype *krb5, const struct krb5_buffer *TK
  */
 extern const struct krb5_crypto_profile rfc3961_simplified_profile;
 
+int crypto_shash_update_sg(struct shash_desc *desc, struct scatterlist *sg,
+			   size_t offset, size_t len);
 int krb5enc_derive_encrypt_keys(const struct krb5_enctype *krb5,
 				const struct krb5_buffer *TK,
 				unsigned int usage,
@@ -156,3 +160,12 @@ int rfc3961_load_checksum_key(const struct krb5_enctype *krb5,
 			      const struct krb5_buffer *Kc,
 			      struct krb5_buffer *setkey,
 			      gfp_t gfp);
+ssize_t krb5_aead_encrypt(const struct krb5_enctype *krb5,
+			  struct crypto_aead *aead,
+			  struct scatterlist *sg, unsigned int nr_sg, size_t sg_len,
+			  size_t data_offset, size_t data_len,
+			  bool preconfounded);
+int krb5_aead_decrypt(const struct krb5_enctype *krb5,
+		      struct crypto_aead *aead,
+		      struct scatterlist *sg, unsigned int nr_sg,
+		      size_t *_offset, size_t *_len);
diff --git a/crypto/krb5/rfc3961_simplified.c b/crypto/krb5/rfc3961_simplified.c
index d25fbd574dde..55f295f3ac5f 100644
--- a/crypto/krb5/rfc3961_simplified.c
+++ b/crypto/krb5/rfc3961_simplified.c
@@ -66,7 +66,10 @@
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
+#include <linux/random.h>
+#include <linux/skbuff.h>
 #include <linux/slab.h>
+#include <linux/highmem.h>
 #include <linux/lcm.h>
 #include <crypto/skcipher.h>
 #include <crypto/hash.h>
@@ -75,6 +78,31 @@
 /* Maximum blocksize for the supported crypto algorithms */
 #define KRB5_MAX_BLOCKSIZE  (16)
 
+int crypto_shash_update_sg(struct shash_desc *desc, struct scatterlist *sg,
+			   size_t offset, size_t len)
+{
+	do {
+		int ret;
+
+		if (offset < sg->length) {
+			struct page *page = sg_page(sg);
+			void *p = kmap_local_page(page);
+			void *q = p + sg->offset + offset;
+			size_t seg = min_t(size_t, len, sg->length - offset);
+
+			ret = crypto_shash_update(desc, q, seg);
+			kunmap_local(p);
+			if (ret < 0)
+				return ret;
+			len -= seg;
+			offset = 0;
+		} else {
+			offset -= sg->length;
+		}
+	} while (len > 0 && (sg = sg_next(sg)));
+	return 0;
+}
+
 static int rfc3961_do_encrypt(struct crypto_sync_skcipher *tfm, void *iv,
 			      const struct krb5_buffer *in, struct krb5_buffer *out)
 {
@@ -503,6 +531,122 @@ int rfc3961_load_checksum_key(const struct krb5_enctype *krb5,
 		return -ENOMEM;
 	return 0;
 }
+
+/*
+ * Apply encryption and checksumming functions to part of a scatterlist.
+ */
+ssize_t krb5_aead_encrypt(const struct krb5_enctype *krb5,
+			  struct crypto_aead *aead,
+			  struct scatterlist *sg, unsigned int nr_sg, size_t sg_len,
+			  size_t data_offset, size_t data_len,
+			  bool preconfounded)
+{
+	struct aead_request *req;
+	ssize_t ret, done;
+	size_t bsize, base_len, secure_offset, secure_len, pad_len, cksum_offset;
+	void *buffer;
+	u8 *iv;
+
+	if (WARN_ON(data_offset != krb5->conf_len))
+		return -EINVAL; /* Data is in wrong place */
+
+	secure_offset	= 0;
+	base_len	= krb5->conf_len + data_len;
+	pad_len		= 0;
+	secure_len	= base_len + pad_len;
+	cksum_offset	= secure_len;
+	if (WARN_ON(cksum_offset + krb5->cksum_len > sg_len))
+		return -EFAULT;
+
+	bsize = krb5_aead_size(aead) +
+		krb5_aead_ivsize(aead);
+	buffer = kzalloc(bsize, GFP_NOFS);
+	if (!buffer)
+		return -ENOMEM;
+
+	/* Insert the confounder into the buffer */
+	ret = -EFAULT;
+	if (!preconfounded) {
+		get_random_bytes(buffer, krb5->conf_len);
+		done = sg_pcopy_from_buffer(sg, nr_sg, buffer, krb5->conf_len,
+					    secure_offset);
+		if (done != krb5->conf_len)
+			goto error;
+	}
+
+	/* We may need to pad out to the crypto blocksize. */
+	if (pad_len) {
+		done = sg_zero_buffer(sg, nr_sg, pad_len, data_offset + data_len);
+		if (done != pad_len)
+			goto error;
+	}
+
+	/* Hash and encrypt the message. */
+	req = buffer;
+	iv = buffer + krb5_aead_size(aead);
+
+	aead_request_set_tfm(req, aead);
+	aead_request_set_callback(req, 0, NULL, NULL);
+	aead_request_set_crypt(req, sg, sg, secure_len, iv);
+	ret = crypto_aead_encrypt(req);
+	if (ret < 0)
+		goto error;
+
+	ret = secure_len + krb5->cksum_len;
+
+error:
+	kfree_sensitive(buffer);
+	return ret;
+}
+
+/*
+ * Apply decryption and checksumming functions to a message.  The offset and
+ * length are updated to reflect the actual content of the encrypted region.
+ */
+int krb5_aead_decrypt(const struct krb5_enctype *krb5,
+		      struct crypto_aead *aead,
+		      struct scatterlist *sg, unsigned int nr_sg,
+		      size_t *_offset, size_t *_len)
+{
+	struct aead_request *req;
+	size_t bsize;
+	void *buffer;
+	int ret;
+	u8 *iv;
+
+	if (WARN_ON(*_offset != 0))
+		return -EINVAL; /* Can't set offset on aead */
+
+	if (*_len < krb5->conf_len + krb5->cksum_len)
+		return -EPROTO;
+
+	bsize = krb5_aead_size(aead) +
+		krb5_aead_ivsize(aead);
+	buffer = kzalloc(bsize, GFP_NOFS);
+	if (!buffer)
+		return -ENOMEM;
+
+	/* Decrypt the message and verify its checksum. */
+	req = buffer;
+	iv = buffer + krb5_aead_size(aead);
+
+	aead_request_set_tfm(req, aead);
+	aead_request_set_callback(req, 0, NULL, NULL);
+	aead_request_set_crypt(req, sg, sg, *_len, iv);
+	ret = crypto_aead_decrypt(req);
+	if (ret < 0)
+		goto error;
+
+	/* Adjust the boundaries of the data. */
+	*_offset += krb5->conf_len;
+	*_len -= krb5->conf_len + krb5->cksum_len;
+	ret = 0;
+
+error:
+	kfree_sensitive(buffer);
+	return ret;
+}
+
 const struct krb5_crypto_profile rfc3961_simplified_profile = {
 	.calc_PRF		= rfc3961_calc_PRF,
 	.calc_Kc		= rfc3961_calc_DK,
@@ -512,4 +656,6 @@ const struct krb5_crypto_profile rfc3961_simplified_profile = {
 	.load_encrypt_keys	= krb5enc_load_encrypt_keys,
 	.derive_checksum_key	= rfc3961_derive_checksum_key,
 	.load_checksum_key	= rfc3961_load_checksum_key,
+	.encrypt		= krb5_aead_encrypt,
+	.decrypt		= krb5_aead_decrypt,
 };



From dhowells at redhat.com  Fri Jan 17 10:35:22 2025
From: dhowells at redhat.com (David Howells)
Date: Fri, 17 Jan 2025 18:35:22 +0000
Subject: [RFC PATCH 13/24] crypto/krb5: Implement the Kerberos5 rfc3961 get_mic and verify_mic
In-Reply-To: <20250117183538.881618-1-dhowells@redhat.com>
References: <20250117183538.881618-1-dhowells@redhat.com>
Message-ID: <20250117183538.881618-14-dhowells@redhat.com>

Add functions that sign and verify a message according to rfc3961 sec 5.4,
using Kc to generate a checksum and insert it into the MIC field in the
skbuff in the sign phase then checksum the data and compare it to the MIC
in the verify phase.

Signed-off-by: David Howells <dhowells at redhat.com>
cc: Herbert Xu <herbert at gondor.apana.org.au>
cc: "David S. Miller" <davem at davemloft.net>
cc: Chuck Lever <chuck.lever at oracle.com>
cc: Marc Dionne <marc.dionne at auristor.com>
cc: Eric Dumazet <edumazet at google.com>
cc: Jakub Kicinski <kuba at kernel.org>
cc: Paolo Abeni <pabeni at redhat.com>
cc: Simon Horman <horms at kernel.org>
cc: linux-afs at lists.infradead.org
cc: linux-nfs at vger.kernel.org
cc: linux-crypto at vger.kernel.org
cc: netdev at vger.kernel.org
---
 crypto/krb5/internal.h           |  10 +++
 crypto/krb5/rfc3961_simplified.c | 130 +++++++++++++++++++++++++++++++
 2 files changed, 140 insertions(+)

diff --git a/crypto/krb5/internal.h b/crypto/krb5/internal.h
index 4533fb342953..eaaa9c13c8b3 100644
--- a/crypto/krb5/internal.h
+++ b/crypto/krb5/internal.h
@@ -169,3 +169,13 @@ int krb5_aead_decrypt(const struct krb5_enctype *krb5,
 		      struct crypto_aead *aead,
 		      struct scatterlist *sg, unsigned int nr_sg,
 		      size_t *_offset, size_t *_len);
+ssize_t rfc3961_get_mic(const struct krb5_enctype *krb5,
+			struct crypto_shash *shash,
+			const struct krb5_buffer *metadata,
+			struct scatterlist *sg, unsigned int nr_sg, size_t sg_len,
+			size_t data_offset, size_t data_len);
+int rfc3961_verify_mic(const struct krb5_enctype *krb5,
+		       struct crypto_shash *shash,
+		       const struct krb5_buffer *metadata,
+		       struct scatterlist *sg, unsigned int nr_sg,
+		       size_t *_offset, size_t *_len);
diff --git a/crypto/krb5/rfc3961_simplified.c b/crypto/krb5/rfc3961_simplified.c
index 55f295f3ac5f..59f0e7c823f7 100644
--- a/crypto/krb5/rfc3961_simplified.c
+++ b/crypto/krb5/rfc3961_simplified.c
@@ -647,6 +647,134 @@ int krb5_aead_decrypt(const struct krb5_enctype *krb5,
 	return ret;
 }
 
+/*
+ * Generate a checksum over some metadata and part of an skbuff and insert the
+ * MIC into the skbuff immediately prior to the data.
+ */
+ssize_t rfc3961_get_mic(const struct krb5_enctype *krb5,
+			struct crypto_shash *shash,
+			const struct krb5_buffer *metadata,
+			struct scatterlist *sg, unsigned int nr_sg, size_t sg_len,
+			size_t data_offset, size_t data_len)
+{
+	struct shash_desc *desc;
+	ssize_t ret, done;
+	size_t bsize;
+	void *buffer, *digest;
+
+	if (WARN_ON(data_offset != krb5->cksum_len))
+		return -EMSGSIZE;
+
+	bsize = krb5_shash_size(shash) +
+		krb5_digest_size(shash);
+	buffer = kzalloc(bsize, GFP_NOFS);
+	if (!buffer)
+		return -ENOMEM;
+
+	/* Calculate the MIC with key Kc and store it into the skb */
+	desc = buffer;
+	desc->tfm = shash;
+	ret = crypto_shash_init(desc);
+	if (ret < 0)
+		goto error;
+
+	if (metadata) {
+		ret = crypto_shash_update(desc, metadata->data, metadata->len);
+		if (ret < 0)
+			goto error;
+	}
+
+	ret = crypto_shash_update_sg(desc, sg, data_offset, data_len);
+	if (ret < 0)
+		goto error;
+
+	digest = buffer + krb5_shash_size(shash);
+	ret = crypto_shash_final(desc, digest);
+	if (ret < 0)
+		goto error;
+
+	ret = -EFAULT;
+	done = sg_pcopy_from_buffer(sg, nr_sg, digest, krb5->cksum_len,
+				    data_offset - krb5->cksum_len);
+	if (done != krb5->cksum_len)
+		goto error;
+
+	ret = krb5->cksum_len + data_len;
+
+error:
+	kfree_sensitive(buffer);
+	return ret;
+}
+
+/*
+ * Check the MIC on a region of an skbuff.  The offset and length are updated
+ * to reflect the actual content of the secure region.
+ */
+int rfc3961_verify_mic(const struct krb5_enctype *krb5,
+		       struct crypto_shash *shash,
+		       const struct krb5_buffer *metadata,
+		       struct scatterlist *sg, unsigned int nr_sg,
+		       size_t *_offset, size_t *_len)
+{
+	struct shash_desc *desc;
+	ssize_t done;
+	size_t bsize, data_offset, data_len, offset = *_offset, len = *_len;
+	void *buffer = NULL;
+	int ret;
+	u8 *cksum, *cksum2;
+
+	if (len < krb5->cksum_len)
+		return -EPROTO;
+	data_offset = offset + krb5->cksum_len;
+	data_len = len - krb5->cksum_len;
+
+	bsize = krb5_shash_size(shash) +
+		krb5_digest_size(shash) * 2;
+	buffer = kzalloc(bsize, GFP_NOFS);
+	if (!buffer)
+		return -ENOMEM;
+
+	cksum = buffer +
+		krb5_shash_size(shash);
+	cksum2 = buffer +
+		krb5_shash_size(shash) +
+		krb5_digest_size(shash);
+
+	/* Calculate the MIC */
+	desc = buffer;
+	desc->tfm = shash;
+	ret = crypto_shash_init(desc);
+	if (ret < 0)
+		goto error;
+
+	if (metadata) {
+		ret = crypto_shash_update(desc, metadata->data, metadata->len);
+		if (ret < 0)
+			goto error;
+	}
+
+	crypto_shash_update_sg(desc, sg, data_offset, data_len);
+	crypto_shash_final(desc, cksum);
+
+	ret = -EFAULT;
+	done = sg_pcopy_to_buffer(sg, nr_sg, cksum2, krb5->cksum_len, offset);
+	if (done != krb5->cksum_len)
+		goto error;
+
+	if (memcmp(cksum, cksum2, krb5->cksum_len) != 0) {
+		ret = -EBADMSG;
+		goto error;
+	}
+
+	*_offset += krb5->cksum_len;
+	*_len -= krb5->cksum_len;
+	ret = 0;
+
+error:
+	kfree_sensitive(buffer);
+	return ret;
+}
+
 const struct krb5_crypto_profile rfc3961_simplified_profile = {
 	.calc_PRF		= rfc3961_calc_PRF,
 	.calc_Kc		= rfc3961_calc_DK,
@@ -658,4 +786,6 @@ const struct krb5_crypto_profile rfc3961_simplified_profile = {
 	.load_checksum_key	= rfc3961_load_checksum_key,
 	.encrypt		= krb5_aead_encrypt,
 	.decrypt		= krb5_aead_decrypt,
+	.get_mic		= rfc3961_get_mic,
+	.verify_mic		= rfc3961_verify_mic,
 };



From dhowells at redhat.com  Fri Jan 17 10:35:23 2025
From: dhowells at redhat.com (David Howells)
Date: Fri, 17 Jan 2025 18:35:23 +0000
Subject: [RFC PATCH 14/24] crypto/krb5: Implement the AES enctypes from rfc3962
In-Reply-To: <20250117183538.881618-1-dhowells@redhat.com>
References: <20250117183538.881618-1-dhowells@redhat.com>
Message-ID: <20250117183538.881618-15-dhowells@redhat.com>

Implement the aes128-cts-hmac-sha1-96 and aes256-cts-hmac-sha1-96 enctypes
from rfc3962, using the rfc3961 kerberos 5 simplified crypto scheme.

Signed-off-by: David Howells <dhowells at redhat.com>
cc: Herbert Xu <herbert at gondor.apana.org.au>
cc: "David S. Miller" <davem at davemloft.net>
cc: Chuck Lever <chuck.lever at oracle.com>
cc: Marc Dionne <marc.dionne at auristor.com>
cc: Eric Dumazet <edumazet at google.com>
cc: Jakub Kicinski <kuba at kernel.org>
cc: Paolo Abeni <pabeni at redhat.com>
cc: Simon Horman <horms at kernel.org>
cc: linux-afs at lists.infradead.org
cc: linux-nfs at vger.kernel.org
cc: linux-crypto at vger.kernel.org
cc: netdev at vger.kernel.org
---
 crypto/krb5/Kconfig       |   1 +
 crypto/krb5/Makefile      |   3 +-
 crypto/krb5/internal.h    |   6 ++
 crypto/krb5/krb5_api.c    |   2 +
 crypto/krb5/rfc3962_aes.c | 115 ++++++++++++++++++++++++++++++++++++++
 5 files changed, 126 insertions(+), 1 deletion(-)
 create mode 100644 crypto/krb5/rfc3962_aes.c

diff --git a/crypto/krb5/Kconfig b/crypto/krb5/Kconfig
index 079873618abf..2ad874990dc8 100644
--- a/crypto/krb5/Kconfig
+++ b/crypto/krb5/Kconfig
@@ -5,6 +5,7 @@ config CRYPTO_KRB5
 	select CRYPTO_AUTHENC
 	select CRYPTO_SKCIPHER
 	select CRYPTO_HASH_INFO
+	select CRYPTO_HMAC
 	select CRYPTO_SHA1
 	select CRYPTO_CBC
 	select CRYPTO_CTS
diff --git a/crypto/krb5/Makefile b/crypto/krb5/Makefile
index 8dad8e3bf086..35f21411abf8 100644
--- a/crypto/krb5/Makefile
+++ b/crypto/krb5/Makefile
@@ -6,6 +6,7 @@
 krb5-y += \
 	krb5_kdf.o \
 	krb5_api.o \
-	rfc3961_simplified.o
+	rfc3961_simplified.o \
+	rfc3962_aes.o
 
 obj-$(CONFIG_CRYPTO_KRB5) += krb5.o
diff --git a/crypto/krb5/internal.h b/crypto/krb5/internal.h
index eaaa9c13c8b3..267ca978bda5 100644
--- a/crypto/krb5/internal.h
+++ b/crypto/krb5/internal.h
@@ -179,3 +179,9 @@ int rfc3961_verify_mic(const struct krb5_enctype *krb5,
 		       const struct krb5_buffer *metadata,
 		       struct scatterlist *sg, unsigned int nr_sg,
 		       size_t *_offset, size_t *_len);
+
+/*
+ * rfc3962_aes.c
+ */
+extern const struct krb5_enctype krb5_aes128_cts_hmac_sha1_96;
+extern const struct krb5_enctype krb5_aes256_cts_hmac_sha1_96;
diff --git a/crypto/krb5/krb5_api.c b/crypto/krb5/krb5_api.c
index 8fc3a1b9d4ad..ecc6655953d5 100644
--- a/crypto/krb5/krb5_api.c
+++ b/crypto/krb5/krb5_api.c
@@ -17,6 +17,8 @@ MODULE_AUTHOR("Red Hat, Inc.");
 MODULE_LICENSE("GPL");
 
 static const struct krb5_enctype *const krb5_supported_enctypes[] = {
+	&krb5_aes128_cts_hmac_sha1_96,
+	&krb5_aes256_cts_hmac_sha1_96,
 };
 
 /**
diff --git a/crypto/krb5/rfc3962_aes.c b/crypto/krb5/rfc3962_aes.c
new file mode 100644
index 000000000000..5cbf8f4638b9
--- /dev/null
+++ b/crypto/krb5/rfc3962_aes.c
@@ -0,0 +1,115 @@
+// SPDX-License-Identifier: BSD-3-Clause
+/* rfc3962 Advanced Encryption Standard (AES) Encryption for Kerberos 5
+ *
+ * Parts borrowed from net/sunrpc/auth_gss/.
+ */
+/*
+ * COPYRIGHT (c) 2008
+ * The Regents of the University of Michigan
+ * ALL RIGHTS RESERVED
+ *
+ * Permission is granted to use, copy, create derivative works
+ * and redistribute this software and such derivative works
+ * for any purpose, so long as the name of The University of
+ * Michigan is not used in any advertising or publicity
+ * pertaining to the use of distribution of this software
+ * without specific, written prior authorization.  If the
+ * above copyright notice or any other identification of the
+ * University of Michigan is included in any copy of any
+ * portion of this software, then the disclaimer below must
+ * also be included.
+ *
+ * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION
+ * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY
+ * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF
+ * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
+ * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE
+ * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
+ * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGES.
+ */
+
+/*
+ * Copyright (C) 1998 by the FundsXpress, INC.
+ *
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may require
+ * a specific license from the United States Government.  It is the
+ * responsibility of any person or organization contemplating export to
+ * obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of FundsXpress. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  FundsXpress makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/*
+ * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells at redhat.com)
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "internal.h"
+
+const struct krb5_enctype krb5_aes128_cts_hmac_sha1_96 = {
+	.etype		= KRB5_ENCTYPE_AES128_CTS_HMAC_SHA1_96,
+	.ctype		= KRB5_CKSUMTYPE_HMAC_SHA1_96_AES128,
+	.name		= "aes128-cts-hmac-sha1-96",
+	.encrypt_name	= "krb5enc(hmac(sha1),cts(cbc(aes)))",
+	.cksum_name	= "hmac(sha1)",
+	.hash_name	= "sha1",
+	.derivation_enc	= "cts(cbc(aes))",
+	.key_bytes	= 16,
+	.key_len	= 16,
+	.Kc_len		= 16,
+	.Ke_len		= 16,
+	.Ki_len		= 16,
+	.block_len	= 16,
+	.conf_len	= 16,
+	.cksum_len	= 12,
+	.hash_len	= 20,
+	.prf_len	= 16,
+	.keyed_cksum	= true,
+	.random_to_key	= NULL, /* Identity */
+	.profile	= &rfc3961_simplified_profile,
+};
+
+const struct krb5_enctype krb5_aes256_cts_hmac_sha1_96 = {
+	.etype		= KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96,
+	.ctype		= KRB5_CKSUMTYPE_HMAC_SHA1_96_AES256,
+	.name		= "aes256-cts-hmac-sha1-96",
+	.encrypt_name	= "krb5enc(hmac(sha1),cts(cbc(aes)))",
+	.cksum_name	= "hmac(sha1)",
+	.hash_name	= "sha1",
+	.derivation_enc	= "cts(cbc(aes))",
+	.key_bytes	= 32,
+	.key_len	= 32,
+	.Kc_len		= 32,
+	.Ke_len		= 32,
+	.Ki_len		= 32,
+	.block_len	= 16,
+	.conf_len	= 16,
+	.cksum_len	= 12,
+	.hash_len	= 20,
+	.prf_len	= 16,
+	.keyed_cksum	= true,
+	.random_to_key	= NULL, /* Identity */
+	.profile	= &rfc3961_simplified_profile,
+};



From dhowells at redhat.com  Fri Jan 17 10:35:24 2025
From: dhowells at redhat.com (David Howells)
Date: Fri, 17 Jan 2025 18:35:24 +0000
Subject: [RFC PATCH 15/24] crypto/krb5: Implement the AES enctypes from rfc8009
In-Reply-To: <20250117183538.881618-1-dhowells@redhat.com>
References: <20250117183538.881618-1-dhowells@redhat.com>
Message-ID: <20250117183538.881618-16-dhowells@redhat.com>

Implement the aes128-cts-hmac-sha256-128 and aes256-cts-hmac-sha384-192
enctypes from rfc8009, overriding the rfc3961 kerberos 5 simplified crypto
scheme.

Signed-off-by: David Howells <dhowells at redhat.com>
cc: Herbert Xu <herbert at gondor.apana.org.au>
cc: "David S. Miller" <davem at davemloft.net>
cc: Chuck Lever <chuck.lever at oracle.com>
cc: Marc Dionne <marc.dionne at auristor.com>
cc: Eric Dumazet <edumazet at google.com>
cc: Jakub Kicinski <kuba at kernel.org>
cc: Paolo Abeni <pabeni at redhat.com>
cc: Simon Horman <horms at kernel.org>
cc: linux-afs at lists.infradead.org
cc: linux-nfs at vger.kernel.org
cc: linux-crypto at vger.kernel.org
cc: netdev at vger.kernel.org
---
 crypto/krb5/Kconfig        |   2 +
 crypto/krb5/Makefile       |   3 +-
 crypto/krb5/internal.h     |  16 ++
 crypto/krb5/krb5_api.c     |   2 +
 crypto/krb5/rfc8009_aes2.c | 296 +++++++++++++++++++++++++++++++++++++
 include/crypto/krb5.h      |   4 +
 6 files changed, 322 insertions(+), 1 deletion(-)
 create mode 100644 crypto/krb5/rfc8009_aes2.c

diff --git a/crypto/krb5/Kconfig b/crypto/krb5/Kconfig
index 2ad874990dc8..52f0ed2d7820 100644
--- a/crypto/krb5/Kconfig
+++ b/crypto/krb5/Kconfig
@@ -7,6 +7,8 @@ config CRYPTO_KRB5
 	select CRYPTO_HASH_INFO
 	select CRYPTO_HMAC
 	select CRYPTO_SHA1
+	select CRYPTO_SHA256
+	select CRYPTO_SHA512
 	select CRYPTO_CBC
 	select CRYPTO_CTS
 	select CRYPTO_AES
diff --git a/crypto/krb5/Makefile b/crypto/krb5/Makefile
index 35f21411abf8..7fd215ec3a85 100644
--- a/crypto/krb5/Makefile
+++ b/crypto/krb5/Makefile
@@ -7,6 +7,7 @@ krb5-y += \
 	krb5_kdf.o \
 	krb5_api.o \
 	rfc3961_simplified.o \
-	rfc3962_aes.o
+	rfc3962_aes.o \
+	rfc8009_aes2.o
 
 obj-$(CONFIG_CRYPTO_KRB5) += krb5.o
diff --git a/crypto/krb5/internal.h b/crypto/krb5/internal.h
index 267ca978bda5..81bf38cf12fc 100644
--- a/crypto/krb5/internal.h
+++ b/crypto/krb5/internal.h
@@ -185,3 +185,19 @@ int rfc3961_verify_mic(const struct krb5_enctype *krb5,
  */
 extern const struct krb5_enctype krb5_aes128_cts_hmac_sha1_96;
 extern const struct krb5_enctype krb5_aes256_cts_hmac_sha1_96;
+
+/*
+ * rfc8009_aes2.c
+ */
+extern const struct krb5_enctype krb5_aes128_cts_hmac_sha256_128;
+extern const struct krb5_enctype krb5_aes256_cts_hmac_sha384_192;
+int authenc_derive_encrypt_keys(const struct krb5_enctype *krb5,
+				const struct krb5_buffer *TK,
+				unsigned int usage,
+				struct krb5_buffer *setkey,
+				gfp_t gfp);
+int authenc_load_encrypt_keys(const struct krb5_enctype *krb5,
+			      const struct krb5_buffer *Ke,
+			      const struct krb5_buffer *Ki,
+			      struct krb5_buffer *setkey,
+			      gfp_t gfp);
diff --git a/crypto/krb5/krb5_api.c b/crypto/krb5/krb5_api.c
index ecc6655953d5..5b94cc5db461 100644
--- a/crypto/krb5/krb5_api.c
+++ b/crypto/krb5/krb5_api.c
@@ -19,6 +19,8 @@ MODULE_LICENSE("GPL");
 static const struct krb5_enctype *const krb5_supported_enctypes[] = {
 	&krb5_aes128_cts_hmac_sha1_96,
 	&krb5_aes256_cts_hmac_sha1_96,
+	&krb5_aes128_cts_hmac_sha256_128,
+	&krb5_aes256_cts_hmac_sha384_192,
 };
 
 /**
diff --git a/crypto/krb5/rfc8009_aes2.c b/crypto/krb5/rfc8009_aes2.c
new file mode 100644
index 000000000000..d7225187ce8b
--- /dev/null
+++ b/crypto/krb5/rfc8009_aes2.c
@@ -0,0 +1,296 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* rfc8009 AES Encryption with HMAC-SHA2 for Kerberos 5
+ *
+ * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells at redhat.com)
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/slab.h>
+#include <linux/rtnetlink.h>
+#include <crypto/authenc.h>
+#include "internal.h"
+
+static const struct krb5_buffer rfc8009_no_context = { .len = 0, .data = "" };
+
+/*
+ * Calculate the key derivation function KDF-HMAC-SHA2(key, label, [context,] k)
+ *
+ *	KDF-HMAC-SHA2(key, label, [context,] k) = k-truncate(K1)
+ *
+ *	Using the appropriate one of:
+ *		K1 = HMAC-SHA-256(key, 0x00000001 | label | 0x00 | k)
+ *		K1 = HMAC-SHA-384(key, 0x00000001 | label | 0x00 | k)
+ *		K1 = HMAC-SHA-256(key, 0x00000001 | label | 0x00 | context | k)
+ *		K1 = HMAC-SHA-384(key, 0x00000001 | label | 0x00 | context | k)
+ *	[rfc8009 sec 3]
+ */
+static int rfc8009_calc_KDF_HMAC_SHA2(const struct krb5_enctype *krb5,
+				      const struct krb5_buffer *key,
+				      const struct krb5_buffer *label,
+				      const struct krb5_buffer *context,
+				      unsigned int k,
+				      struct krb5_buffer *result,
+				      gfp_t gfp)
+{
+	struct crypto_shash *shash;
+	struct krb5_buffer K1, data;
+	struct shash_desc *desc;
+	__be32 tmp;
+	size_t bsize;
+	void *buffer;
+	u8 *p;
+	int ret = -ENOMEM;
+
+	if (WARN_ON(result->len != k / 8))
+		return -EINVAL;
+
+	shash = crypto_alloc_shash(krb5->cksum_name, 0, 0);
+	if (IS_ERR(shash))
+		return (PTR_ERR(shash) == -ENOENT) ? -ENOPKG : PTR_ERR(shash);
+	ret = crypto_shash_setkey(shash, key->data, key->len);
+	if (ret < 0)
+		goto error_shash;
+
+	ret = -EINVAL;
+	if (WARN_ON(crypto_shash_digestsize(shash) * 8 < k))
+		goto error_shash;
+
+	ret = -ENOMEM;
+	data.len = 4 + label->len + 1 + context->len + 4;
+	bsize = krb5_shash_size(shash) +
+		krb5_digest_size(shash) +
+		crypto_roundup(data.len);
+	buffer = kzalloc(bsize, GFP_NOFS);
+	if (!buffer)
+		goto error_shash;
+
+	desc = buffer;
+	desc->tfm = shash;
+	ret = crypto_shash_init(desc);
+	if (ret < 0)
+		goto error;
+
+	p = data.data = buffer +
+		krb5_shash_size(shash) +
+		krb5_digest_size(shash);
+	*(__be32 *)p = htonl(0x00000001);
+	p += 4;
+	memcpy(p, label->data, label->len);
+	p += label->len;
+	*p++ = 0;
+	memcpy(p, context->data, context->len);
+	p += context->len;
+	tmp = htonl(k);
+	memcpy(p, &tmp, 4);
+	p += 4;
+
+	ret = -EINVAL;
+	if (WARN_ON(p - (u8 *)data.data != data.len))
+		goto error;
+
+	K1.len = crypto_shash_digestsize(shash);
+	K1.data = buffer +
+		krb5_shash_size(shash);
+
+	ret = crypto_shash_finup(desc, data.data, data.len, K1.data);
+	if (ret < 0)
+		goto error;
+
+	memcpy(result->data, K1.data, result->len);
+
+error:
+	kfree_sensitive(buffer);
+error_shash:
+	crypto_free_shash(shash);
+	return ret;
+}
+
+/*
+ * Calculate the pseudo-random function, PRF().
+ *
+ *	PRF = KDF-HMAC-SHA2(input-key, "prf", octet-string, 256)
+ *	PRF = KDF-HMAC-SHA2(input-key, "prf", octet-string, 384)
+ *
+ *      The "prfconstant" used in the PRF operation is the three-octet string
+ *      "prf".
+ *      [rfc8009 sec 5]
+ */
+static int rfc8009_calc_PRF(const struct krb5_enctype *krb5,
+			    const struct krb5_buffer *input_key,
+			    const struct krb5_buffer *octet_string,
+			    struct krb5_buffer *result,
+			    gfp_t gfp)
+{
+	static const struct krb5_buffer prfconstant = { 3, "prf" };
+
+	return rfc8009_calc_KDF_HMAC_SHA2(krb5, input_key, &prfconstant,
+					  octet_string, krb5->prf_len * 8,
+					  result, gfp);
+}
+
+/*
+ * Derive Ke.
+ *	Ke = KDF-HMAC-SHA2(base-key, usage | 0xAA, 128)
+ *	Ke = KDF-HMAC-SHA2(base-key, usage | 0xAA, 256)
+ *      [rfc8009 sec 5]
+ */
+static int rfc8009_calc_Ke(const struct krb5_enctype *krb5,
+			   const struct krb5_buffer *base_key,
+			   const struct krb5_buffer *usage_constant,
+			   struct krb5_buffer *result,
+			   gfp_t gfp)
+{
+	return rfc8009_calc_KDF_HMAC_SHA2(krb5, base_key, usage_constant,
+					  &rfc8009_no_context, krb5->key_bytes * 8,
+					  result, gfp);
+}
+
+/*
+ * Derive Kc/Ki
+ *	Kc = KDF-HMAC-SHA2(base-key, usage | 0x99, 128)
+ *	Ki = KDF-HMAC-SHA2(base-key, usage | 0x55, 128)
+ *	Kc = KDF-HMAC-SHA2(base-key, usage | 0x99, 192)
+ *	Ki = KDF-HMAC-SHA2(base-key, usage | 0x55, 192)
+ *      [rfc8009 sec 5]
+ */
+static int rfc8009_calc_Ki(const struct krb5_enctype *krb5,
+			   const struct krb5_buffer *base_key,
+			   const struct krb5_buffer *usage_constant,
+			   struct krb5_buffer *result,
+			   gfp_t gfp)
+{
+	return rfc8009_calc_KDF_HMAC_SHA2(krb5, base_key, usage_constant,
+					  &rfc8009_no_context, krb5->cksum_len * 8,
+					  result, gfp);
+}
+
+/*
+ * Derive the Ke and Ki keys and package them into a key parameter that can be
+ * given to the setkey of a authenc AEAD crypto object.
+ */
+int authenc_derive_encrypt_keys(const struct krb5_enctype *krb5,
+				const struct krb5_buffer *TK,
+				unsigned int usage,
+				struct krb5_buffer *setkey,
+				gfp_t gfp)
+{
+	struct krb5_buffer Ke, Ki;
+	__be32 *khdr = NULL;
+	int ret;
+
+	Ke.len  = krb5->Ke_len;
+	Ki.len  = krb5->Ki_len;
+	setkey->len = sizeof(__be32) * 3 + krb5->Ke_len + krb5->Ki_len;
+	setkey->data = kzalloc(setkey->len, GFP_KERNEL);
+	if (!setkey->data)
+		return -ENOMEM;
+
+	khdr = setkey->data;
+	Ke.data = setkey->data + 12;
+	Ki.data = setkey->data + 12 + Ke.len;
+
+	khdr[0] = htonl(1); /* Format 1 */
+	khdr[1] = htonl(Ke.len);
+	khdr[2] = htonl(Ki.len);
+
+	ret = krb5_derive_Ke(krb5, TK, usage, &Ke, gfp);
+	if (ret < 0) {
+		pr_err("get_Ke failed %d\n", ret);
+		return ret;
+	}
+	ret = krb5_derive_Ki(krb5, TK, usage, &Ki, gfp);
+	if (ret < 0)
+		pr_err("get_Ki failed %d\n", ret);
+	return ret;
+}
+
+/*
+ * Package predefined Ke and Ki keys and into a key parameter that can be given
+ * to the setkey of an authenc AEAD crypto object.
+ */
+int authenc_load_encrypt_keys(const struct krb5_enctype *krb5,
+			      const struct krb5_buffer *Ke,
+			      const struct krb5_buffer *Ki,
+			      struct krb5_buffer *setkey,
+			      gfp_t gfp)
+{
+	struct crypto_authenc_key_param *param;
+	struct rtattr *rta;
+
+	setkey->len = RTA_LENGTH(sizeof(*param)) + Ke->len + Ki->len;
+	setkey->data = kzalloc(setkey->len, GFP_KERNEL);
+	if (!setkey->data)
+		return -ENOMEM;
+
+	rta = setkey->data;
+	rta->rta_type = CRYPTO_AUTHENC_KEYA_PARAM;
+	rta->rta_len = RTA_LENGTH(sizeof(*param));
+	param = RTA_DATA(rta);
+	param->enckeylen = htonl(Ke->len);
+	memcpy((void *)(param + 1), Ki->data, Ki->len);
+	memcpy((void *)(param + 1) + Ki->len, Ke->data, Ke->len);
+	return 0;
+}
+
+static const struct krb5_crypto_profile rfc8009_crypto_profile = {
+	.calc_PRF		= rfc8009_calc_PRF,
+	.calc_Kc		= rfc8009_calc_Ki,
+	.calc_Ke		= rfc8009_calc_Ke,
+	.calc_Ki		= rfc8009_calc_Ki,
+	.derive_encrypt_keys	= authenc_derive_encrypt_keys,
+	.load_encrypt_keys	= authenc_load_encrypt_keys,
+	.derive_checksum_key	= rfc3961_derive_checksum_key,
+	.load_checksum_key	= rfc3961_load_checksum_key,
+	.encrypt		= NULL, //rfc8009_encrypt,
+	.decrypt		= NULL, //rfc8009_decrypt,
+	.get_mic		= rfc3961_get_mic,
+	.verify_mic		= rfc3961_verify_mic,
+};
+
+const struct krb5_enctype krb5_aes128_cts_hmac_sha256_128 = {
+	.etype		= KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128,
+	.ctype		= KRB5_CKSUMTYPE_HMAC_SHA256_128_AES128,
+	.name		= "aes128-cts-hmac-sha256-128",
+	.encrypt_name	= "authenc(hmac(sha256),cts(cbc(aes)))",
+	.cksum_name	= "hmac(sha256)",
+	.hash_name	= "sha256",
+	.derivation_enc	= "cts(cbc(aes))",
+	.key_bytes	= 16,
+	.key_len	= 16,
+	.Kc_len		= 16,
+	.Ke_len		= 16,
+	.Ki_len		= 16,
+	.block_len	= 16,
+	.conf_len	= 16,
+	.cksum_len	= 16,
+	.hash_len	= 20,
+	.prf_len	= 32,
+	.keyed_cksum	= true,
+	.random_to_key	= NULL, /* Identity */
+	.profile	= &rfc8009_crypto_profile,
+};
+
+const struct krb5_enctype krb5_aes256_cts_hmac_sha384_192 = {
+	.etype		= KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192,
+	.ctype		= KRB5_CKSUMTYPE_HMAC_SHA384_192_AES256,
+	.name		= "aes256-cts-hmac-sha384-192",
+	.encrypt_name	= "authenc(hmac(sha384),cts(cbc(aes)))",
+	.cksum_name	= "hmac(sha384)",
+	.hash_name	= "sha384",
+	.derivation_enc	= "cts(cbc(aes))",
+	.key_bytes	= 32,
+	.key_len	= 32,
+	.Kc_len		= 24,
+	.Ke_len		= 32,
+	.Ki_len		= 24,
+	.block_len	= 16,
+	.conf_len	= 16,
+	.cksum_len	= 24,
+	.hash_len	= 20,
+	.prf_len	= 48,
+	.keyed_cksum	= true,
+	.random_to_key	= NULL, /* Identity */
+	.profile	= &rfc8009_crypto_profile,
+};
diff --git a/include/crypto/krb5.h b/include/crypto/krb5.h
index a24d9ee53675..6921785688bf 100644
--- a/include/crypto/krb5.h
+++ b/include/crypto/krb5.h
@@ -31,6 +31,8 @@ struct scatterlist;
 #define KRB5_ENCTYPE_DES3_CBC_SHA1		0x0010
 #define KRB5_ENCTYPE_AES128_CTS_HMAC_SHA1_96	0x0011
 #define KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96	0x0012
+#define KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128	0x0013
+#define KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192	0x0014
 #define KRB5_ENCTYPE_ARCFOUR_HMAC		0x0017
 #define KRB5_ENCTYPE_ARCFOUR_HMAC_EXP		0x0018
 #define KRB5_ENCTYPE_UNKNOWN			0x01ff
@@ -45,6 +47,8 @@ struct scatterlist;
 #define KRB5_CKSUMTYPE_HMAC_SHA1_DES3		0x000c
 #define KRB5_CKSUMTYPE_HMAC_SHA1_96_AES128	0x000f
 #define KRB5_CKSUMTYPE_HMAC_SHA1_96_AES256	0x0010
+#define KRB5_CKSUMTYPE_HMAC_SHA256_128_AES128	0x0013
+#define KRB5_CKSUMTYPE_HMAC_SHA384_192_AES256	0x0014
 #define KRB5_CKSUMTYPE_HMAC_MD5_ARCFOUR		-138 /* Microsoft md5 hmac cksumtype */
 
 /*



From dhowells at redhat.com  Fri Jan 17 10:35:25 2025
From: dhowells at redhat.com (David Howells)
Date: Fri, 17 Jan 2025 18:35:25 +0000
Subject: [RFC PATCH 16/24] crypto/krb5: Implement the AES encrypt/decrypt from rfc8009
In-Reply-To: <20250117183538.881618-1-dhowells@redhat.com>
References: <20250117183538.881618-1-dhowells@redhat.com>
Message-ID: <20250117183538.881618-17-dhowells@redhat.com>

Implement encryption and decryption functions for AES + HMAC-SHA2 as
described in rfc8009 sec 5.

Signed-off-by: David Howells <dhowells at redhat.com>
cc: Herbert Xu <herbert at gondor.apana.org.au>
cc: "David S. Miller" <davem at davemloft.net>
cc: Chuck Lever <chuck.lever at oracle.com>
cc: Marc Dionne <marc.dionne at auristor.com>
cc: Eric Dumazet <edumazet at google.com>
cc: Jakub Kicinski <kuba at kernel.org>
cc: Paolo Abeni <pabeni at redhat.com>
cc: Simon Horman <horms at kernel.org>
cc: linux-afs at lists.infradead.org
cc: linux-nfs at vger.kernel.org
cc: linux-crypto at vger.kernel.org
cc: netdev at vger.kernel.org
---
 crypto/krb5/rfc8009_aes2.c | 139 ++++++++++++++++++++++++++++++++++++-
 1 file changed, 137 insertions(+), 2 deletions(-)

diff --git a/crypto/krb5/rfc8009_aes2.c b/crypto/krb5/rfc8009_aes2.c
index d7225187ce8b..bcf84284abff 100644
--- a/crypto/krb5/rfc8009_aes2.c
+++ b/crypto/krb5/rfc8009_aes2.c
@@ -234,6 +234,141 @@ int authenc_load_encrypt_keys(const struct krb5_enctype *krb5,
 	return 0;
 }
 
+/*
+ * Apply encryption and checksumming functions to a message.  Unlike for
+ * RFC3961, for RFC8009, we have to chuck the starting IV into the hash first.
+ */
+static ssize_t rfc8009_encrypt(const struct krb5_enctype *krb5,
+			       struct crypto_aead *aead,
+			       struct scatterlist *sg, unsigned int nr_sg, size_t sg_len,
+			       size_t data_offset, size_t data_len,
+			       bool preconfounded)
+{
+	struct aead_request *req;
+	struct scatterlist bsg[2];
+	ssize_t ret, done;
+	size_t bsize, base_len, secure_offset, secure_len, pad_len, cksum_offset;
+	void *buffer;
+	u8 *iv, *ad;
+
+	if (WARN_ON(data_offset != krb5->conf_len))
+		return -EINVAL; /* Data is in wrong place */
+
+	secure_offset	= 0;
+	base_len	= krb5->conf_len + data_len;
+	pad_len		= 0;
+	secure_len	= base_len + pad_len;
+	cksum_offset	= secure_len;
+	if (WARN_ON(cksum_offset + krb5->cksum_len > sg_len))
+		return -EFAULT;
+
+	bsize = krb5_aead_size(aead) +
+		krb5_aead_ivsize(aead) * 2;
+	buffer = kzalloc(bsize, GFP_NOFS);
+	if (!buffer)
+		return -ENOMEM;
+
+	req = buffer;
+	iv = buffer + krb5_aead_size(aead);
+	ad = buffer + krb5_aead_size(aead) + krb5_aead_ivsize(aead);
+
+	/* Insert the confounder into the buffer */
+	ret = -EFAULT;
+	if (!preconfounded) {
+		get_random_bytes(buffer, krb5->conf_len);
+		done = sg_pcopy_from_buffer(sg, nr_sg, buffer, krb5->conf_len,
+					    secure_offset);
+		if (done != krb5->conf_len)
+			goto error;
+	}
+
+	/* We may need to pad out to the crypto blocksize. */
+	if (pad_len) {
+		done = sg_zero_buffer(sg, nr_sg, pad_len, data_offset + data_len);
+		if (done != pad_len)
+			goto error;
+	}
+
+	/* We need to include the starting IV in the hash. */
+	sg_init_table(bsg, 2);
+	sg_set_buf(&bsg[0], ad, krb5_aead_ivsize(aead));
+	sg_chain(bsg, 2, sg);
+
+	/* Hash and encrypt the message. */
+	aead_request_set_tfm(req, aead);
+	aead_request_set_callback(req, 0, NULL, NULL);
+	aead_request_set_ad(req, krb5_aead_ivsize(aead));
+	aead_request_set_crypt(req, bsg, bsg, secure_len, iv);
+	ret = crypto_aead_encrypt(req);
+	if (ret < 0)
+		goto error;
+
+	ret = secure_len + krb5->cksum_len;
+
+error:
+	kfree_sensitive(buffer);
+	return ret;
+}
+
+/*
+ * Apply decryption and checksumming functions to a message.  Unlike for
+ * RFC3961, for RFC8009, we have to chuck the starting IV into the hash first.
+ *
+ * The offset and length are updated to reflect the actual content of the
+ * encrypted region.
+ */
+static int rfc8009_decrypt(const struct krb5_enctype *krb5,
+			   struct crypto_aead *aead,
+			   struct scatterlist *sg, unsigned int nr_sg,
+			   size_t *_offset, size_t *_len)
+{
+	struct aead_request *req;
+	struct scatterlist bsg[2];
+	size_t bsize;
+	void *buffer;
+	int ret;
+	u8 *iv, *ad;
+
+	if (WARN_ON(*_offset != 0))
+		return -EINVAL; /* Can't set offset on aead */
+
+	if (*_len < krb5->conf_len + krb5->cksum_len)
+		return -EPROTO;
+
+	bsize = krb5_aead_size(aead) +
+		krb5_aead_ivsize(aead) * 2;
+	buffer = kzalloc(bsize, GFP_NOFS);
+	if (!buffer)
+		return -ENOMEM;
+
+	req = buffer;
+	iv = buffer + krb5_aead_size(aead);
+	ad = buffer + krb5_aead_size(aead) + krb5_aead_ivsize(aead);
+
+	/* We need to include the starting IV in the hash. */
+	sg_init_table(bsg, 2);
+	sg_set_buf(&bsg[0], ad, krb5_aead_ivsize(aead));
+	sg_chain(bsg, 2, sg);
+
+	/* Decrypt the message and verify its checksum. */
+	aead_request_set_tfm(req, aead);
+	aead_request_set_callback(req, 0, NULL, NULL);
+	aead_request_set_ad(req, krb5_aead_ivsize(aead));
+	aead_request_set_crypt(req, bsg, bsg, *_len, iv);
+	ret = crypto_aead_decrypt(req);
+	if (ret < 0)
+		goto error;
+
+	/* Adjust the boundaries of the data. */
+	*_offset += krb5->conf_len;
+	*_len -= krb5->conf_len + krb5->cksum_len;
+	ret = 0;
+
+error:
+	kfree_sensitive(buffer);
+	return ret;
+}
+
 static const struct krb5_crypto_profile rfc8009_crypto_profile = {
 	.calc_PRF		= rfc8009_calc_PRF,
 	.calc_Kc		= rfc8009_calc_Ki,
@@ -243,8 +378,8 @@ static const struct krb5_crypto_profile rfc8009_crypto_profile = {
 	.load_encrypt_keys	= authenc_load_encrypt_keys,
 	.derive_checksum_key	= rfc3961_derive_checksum_key,
 	.load_checksum_key	= rfc3961_load_checksum_key,
-	.encrypt		= NULL, //rfc8009_encrypt,
-	.decrypt		= NULL, //rfc8009_decrypt,
+	.encrypt		= rfc8009_encrypt,
+	.decrypt		= rfc8009_decrypt,
 	.get_mic		= rfc3961_get_mic,
 	.verify_mic		= rfc3961_verify_mic,
 };



From dhowells at redhat.com  Fri Jan 17 10:35:26 2025
From: dhowells at redhat.com (David Howells)
Date: Fri, 17 Jan 2025 18:35:26 +0000
Subject: [RFC PATCH 17/24] crypto/krb5: Implement crypto self-testing
In-Reply-To: <20250117183538.881618-1-dhowells@redhat.com>
References: <20250117183538.881618-1-dhowells@redhat.com>
Message-ID: <20250117183538.881618-18-dhowells@redhat.com>

Implement self-testing infrastructure to test the pseudo-random function,
key derivation, encryption and checksumming.

Signed-off-by: David Howells <dhowells at redhat.com>
cc: Herbert Xu <herbert at gondor.apana.org.au>
cc: "David S. Miller" <davem at davemloft.net>
cc: Chuck Lever <chuck.lever at oracle.com>
cc: Marc Dionne <marc.dionne at auristor.com>
cc: Eric Dumazet <edumazet at google.com>
cc: Jakub Kicinski <kuba at kernel.org>
cc: Paolo Abeni <pabeni at redhat.com>
cc: Simon Horman <horms at kernel.org>
cc: linux-afs at lists.infradead.org
cc: linux-nfs at vger.kernel.org
cc: linux-crypto at vger.kernel.org
cc: netdev at vger.kernel.org
---
 crypto/krb5/Kconfig         |   7 +
 crypto/krb5/Makefile        |   4 +
 crypto/krb5/internal.h      |  48 ++++
 crypto/krb5/krb5_api.c      |  11 +
 crypto/krb5/selftest.c      | 544 ++++++++++++++++++++++++++++++++++++
 crypto/krb5/selftest_data.c |  38 +++
 6 files changed, 652 insertions(+)
 create mode 100644 crypto/krb5/selftest.c
 create mode 100644 crypto/krb5/selftest_data.c

diff --git a/crypto/krb5/Kconfig b/crypto/krb5/Kconfig
index 52f0ed2d7820..82d7e3b55878 100644
--- a/crypto/krb5/Kconfig
+++ b/crypto/krb5/Kconfig
@@ -15,3 +15,10 @@ config CRYPTO_KRB5
 	help
 	  Provide a library for provision of Kerberos-5-based crypto.  This is
 	  intended for network filesystems to use.
+
+config CRYPTO_KRB5_SELFTESTS
+	bool "Kerberos 5 crypto selftests"
+	depends on CRYPTO_KRB5
+	help
+	  Turn on some self-testing for the kerberos 5 crypto functions.  These
+	  will be performed on module load or boot, if compiled in.
diff --git a/crypto/krb5/Makefile b/crypto/krb5/Makefile
index 7fd215ec3a85..1ad327227744 100644
--- a/crypto/krb5/Makefile
+++ b/crypto/krb5/Makefile
@@ -10,4 +10,8 @@ krb5-y += \
 	rfc3962_aes.o \
 	rfc8009_aes2.o
 
+krb5-$(CONFIG_CRYPTO_KRB5_SELFTESTS) += \
+	selftest.o \
+	selftest_data.o
+
 obj-$(CONFIG_CRYPTO_KRB5) += krb5.o
diff --git a/crypto/krb5/internal.h b/crypto/krb5/internal.h
index 81bf38cf12fc..a40f120b4954 100644
--- a/crypto/krb5/internal.h
+++ b/crypto/krb5/internal.h
@@ -114,6 +114,37 @@ struct krb5_crypto_profile {
 	crypto_roundup(crypto_shash_digestsize(TFM))
 #define round16(x) (((x) + 15) & ~15)
 
+/*
+ * Self-testing data.
+ */
+struct krb5_prf_test {
+	u32 etype;
+	const char *name, *key, *octet, *prf;
+};
+
+struct krb5_key_test_one {
+	u32 use;
+	const char *key;
+};
+
+struct krb5_key_test {
+	u32 etype;
+	const char *name, *key;
+	struct krb5_key_test_one Kc, Ke, Ki;
+};
+
+struct krb5_enc_test {
+	u32 etype;
+	u32 usage;
+	const char *name, *plain, *conf, *K0, *Ke, *Ki, *ct;
+};
+
+struct krb5_mic_test {
+	u32 etype;
+	u32 usage;
+	const char *name, *plain, *K0, *Kc, *mic;
+};
+
 /*
  * krb5_api.c
  */
@@ -201,3 +232,20 @@ int authenc_load_encrypt_keys(const struct krb5_enctype *krb5,
 			      const struct krb5_buffer *Ki,
 			      struct krb5_buffer *setkey,
 			      gfp_t gfp);
+
+/*
+ * selftest.c
+ */
+#ifdef CONFIG_CRYPTO_KRB5_SELFTESTS
+int krb5_selftest(void);
+#else
+static inline int krb5_selftest(void) { return 0; }
+#endif
+
+/*
+ * selftest_data.c
+ */
+extern const struct krb5_prf_test krb5_prf_tests[];
+extern const struct krb5_key_test krb5_key_tests[];
+extern const struct krb5_enc_test krb5_enc_tests[];
+extern const struct krb5_mic_test krb5_mic_tests[];
diff --git a/crypto/krb5/krb5_api.c b/crypto/krb5/krb5_api.c
index 5b94cc5db461..8c2949ac9c07 100644
--- a/crypto/krb5/krb5_api.c
+++ b/crypto/krb5/krb5_api.c
@@ -437,3 +437,14 @@ int crypto_krb5_verify_mic(const struct krb5_enctype *krb5,
 					 _offset, _len);
 }
 EXPORT_SYMBOL(crypto_krb5_verify_mic);
+
+static int __init crypto_krb5_init(void)
+{
+	return krb5_selftest();
+}
+module_init(crypto_krb5_init);
+
+static void __exit crypto_krb5_exit(void)
+{
+}
+module_exit(crypto_krb5_exit);
diff --git a/crypto/krb5/selftest.c b/crypto/krb5/selftest.c
new file mode 100644
index 000000000000..2a81a6315a0d
--- /dev/null
+++ b/crypto/krb5/selftest.c
@@ -0,0 +1,544 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Kerberos library self-testing
+ *
+ * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells at redhat.com)
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/slab.h>
+#include <crypto/skcipher.h>
+#include <crypto/hash.h>
+#include "internal.h"
+
+#define VALID(X) \
+	({								\
+		bool __x = (X);						\
+		if (__x) {						\
+			pr_warn("!!! TESTINVAL %s:%u\n", __FILE__, __LINE__); \
+			ret = -EBADMSG;					\
+		}							\
+		__x;							\
+	})
+
+#define CHECK(X) \
+	({								\
+		bool __x = (X);						\
+		if (__x) {						\
+			pr_warn("!!! TESTFAIL %s:%u\n", __FILE__, __LINE__); \
+			ret = -EBADMSG;					\
+		}							\
+		__x;							\
+	})
+
+enum which_key {
+	TEST_KC, TEST_KE, TEST_KI,
+};
+
+#if 0
+static void dump_sg(struct scatterlist *sg, unsigned int limit)
+{
+	unsigned int index = 0, n = 0;
+
+	for (; sg && limit > 0; sg = sg_next(sg)) {
+		unsigned int off = sg->offset, len = umin(sg->length, limit);
+		const void *p = kmap_local_page(sg_page(sg));
+
+		limit -= len;
+		while (len > 0) {
+			unsigned int part = umin(len, 32);
+
+			pr_notice("[%x] %04x: %*phN\n", n, index, part, p + off);
+			index += part;
+			off += part;
+			len -= part;
+		}
+
+		kunmap_local(p);
+		n++;
+	}
+}
+#endif
+
+static int prep_buf(struct krb5_buffer *buf)
+{
+	buf->data = kmalloc(buf->len, GFP_KERNEL);
+	if (!buf->data)
+		return -ENOMEM;
+	return 0;
+}
+
+#define PREP_BUF(BUF, LEN)					\
+	do {							\
+		(BUF)->len = (LEN);				\
+		ret = prep_buf((BUF));				\
+		if (ret < 0)					\
+			goto out;				\
+	} while (0)
+
+static int load_buf(struct krb5_buffer *buf, const char *from)
+{
+	size_t len = strlen(from);
+	int ret;
+
+	if (len > 1 && from[0] == '\'') {
+		PREP_BUF(buf, len - 1);
+		memcpy(buf->data, from + 1, len - 1);
+		ret = 0;
+		goto out;
+	}
+
+	if (VALID(len & 1))
+		return -EINVAL;
+
+	PREP_BUF(buf, len / 2);
+	ret = hex2bin(buf->data, from, buf->len);
+	if (ret < 0) {
+		VALID(1);
+		goto out;
+	}
+out:
+	return ret;
+}
+
+#define LOAD_BUF(BUF, FROM) do { ret = load_buf(BUF, FROM); if (ret < 0) goto out; } while (0)
+
+static void clear_buf(struct krb5_buffer *buf)
+{
+	kfree(buf->data);
+	buf->len = 0;
+	buf->data = NULL;
+}
+
+/*
+ * Perform a pseudo-random function check.
+ */
+static int krb5_test_one_prf(const struct krb5_prf_test *test)
+{
+	const struct krb5_enctype *krb5 = crypto_krb5_find_enctype(test->etype);
+	struct krb5_buffer key = {}, octet = {}, result = {}, prf = {};
+	int ret;
+
+	if (!krb5)
+		return -EOPNOTSUPP;
+
+	pr_notice("Running %s %s\n", krb5->name, test->name);
+
+	LOAD_BUF(&key,   test->key);
+	LOAD_BUF(&octet, test->octet);
+	LOAD_BUF(&prf,   test->prf);
+	PREP_BUF(&result, krb5->prf_len);
+
+	if (VALID(result.len != prf.len)) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	ret = krb5->profile->calc_PRF(krb5, &key, &octet, &result, GFP_KERNEL);
+	if (ret < 0) {
+		CHECK(1);
+		pr_warn("PRF calculation failed %d\n", ret);
+		goto out;
+	}
+
+	if (memcmp(result.data, prf.data, result.len) != 0) {
+		CHECK(1);
+		ret = -EKEYREJECTED;
+		goto out;
+	}
+
+	ret = 0;
+
+out:
+	clear_buf(&result);
+	clear_buf(&octet);
+	clear_buf(&key);
+	return ret;
+}
+
+/*
+ * Perform a key derivation check.
+ */
+static int krb5_test_key(const struct krb5_enctype *krb5,
+			 const struct krb5_buffer *base_key,
+			 const struct krb5_key_test_one *test,
+			 enum which_key which)
+{
+	struct krb5_buffer key = {}, result = {};
+	int ret;
+
+	LOAD_BUF(&key,   test->key);
+	PREP_BUF(&result, key.len);
+
+	switch (which) {
+	case TEST_KC:
+		ret = krb5_derive_Kc(krb5, base_key, test->use, &result, GFP_KERNEL);
+		break;
+	case TEST_KE:
+		ret = krb5_derive_Ke(krb5, base_key, test->use, &result, GFP_KERNEL);
+		break;
+	case TEST_KI:
+		ret = krb5_derive_Ki(krb5, base_key, test->use, &result, GFP_KERNEL);
+		break;
+	default:
+		VALID(1);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (ret < 0) {
+		CHECK(1);
+		pr_warn("Key derivation failed %d\n", ret);
+		goto out;
+	}
+
+	if (memcmp(result.data, key.data, result.len) != 0) {
+		CHECK(1);
+		ret = -EKEYREJECTED;
+		goto out;
+	}
+
+out:
+	clear_buf(&key);
+	clear_buf(&result);
+	return ret;
+}
+
+static int krb5_test_one_key(const struct krb5_key_test *test)
+{
+	const struct krb5_enctype *krb5 = crypto_krb5_find_enctype(test->etype);
+	struct krb5_buffer base_key = {};
+	int ret;
+
+	if (!krb5)
+		return -EOPNOTSUPP;
+
+	pr_notice("Running %s %s\n", krb5->name, test->name);
+
+	LOAD_BUF(&base_key, test->key);
+
+	ret = krb5_test_key(krb5, &base_key, &test->Kc, TEST_KC);
+	if (ret < 0)
+		goto out;
+	ret = krb5_test_key(krb5, &base_key, &test->Ke, TEST_KE);
+	if (ret < 0)
+		goto out;
+	ret = krb5_test_key(krb5, &base_key, &test->Ki, TEST_KI);
+	if (ret < 0)
+		goto out;
+
+out:
+	clear_buf(&base_key);
+	return ret;
+}
+
+/*
+ * Perform an encryption test.
+ */
+static int krb5_test_one_enc(const struct krb5_enc_test *test, void *buf)
+{
+	const struct krb5_enctype *krb5 = crypto_krb5_find_enctype(test->etype);
+	struct crypto_aead *ci = NULL;
+	struct krb5_buffer K0 = {}, Ke = {}, Ki = {}, keys = {};
+	struct krb5_buffer conf = {}, plain = {}, ct = {};
+	struct scatterlist sg[1];
+	size_t data_len, data_offset, message_len;
+	int ret;
+
+	if (!krb5)
+		return -EOPNOTSUPP;
+
+	pr_notice("Running %s %s\n", krb5->name, test->name);
+
+	/* Load the test data into binary buffers. */
+	LOAD_BUF(&conf, test->conf);
+	LOAD_BUF(&plain, test->plain);
+	LOAD_BUF(&ct, test->ct);
+
+	if (test->K0) {
+		LOAD_BUF(&K0, test->K0);
+	} else {
+		LOAD_BUF(&Ke, test->Ke);
+		LOAD_BUF(&Ki, test->Ki);
+
+		ret = krb5->profile->load_encrypt_keys(krb5, &Ke, &Ki, &keys, GFP_KERNEL);
+		if (ret < 0)
+			goto out;
+	}
+
+	if (VALID(conf.len != krb5->conf_len) ||
+	    VALID(ct.len != krb5->conf_len + plain.len + krb5->cksum_len))
+		goto out;
+
+	data_len = plain.len;
+	message_len = crypto_krb5_how_much_buffer(krb5, KRB5_ENCRYPT_MODE,
+						  data_len, &data_offset);
+
+	if (CHECK(message_len != ct.len)) {
+		pr_warn("Encrypted length mismatch %zu != %u\n", message_len, ct.len);
+		goto out;
+	}
+	if (CHECK(data_offset != conf.len)) {
+		pr_warn("Data offset mismatch %zu != %u\n", data_offset, conf.len);
+		goto out;
+	}
+
+	memcpy(buf, conf.data, conf.len);
+	memcpy(buf + data_offset, plain.data, plain.len);
+
+	/* Allocate a crypto object and set its key. */
+	if (test->K0)
+		ci = crypto_krb5_prepare_encryption(krb5, &K0, test->usage, GFP_KERNEL);
+	else
+		ci = krb5_prepare_encryption(krb5, &keys, GFP_KERNEL);
+
+	if (IS_ERR(ci)) {
+		ret = PTR_ERR(ci);
+		ci = NULL;
+		pr_err("Couldn't alloc AEAD %s: %d\n", krb5->encrypt_name, ret);
+		goto out;
+	}
+
+	/* Encrypt the message. */
+	sg_init_one(sg, buf, message_len);
+	ret = crypto_krb5_encrypt(krb5, ci, sg, 1, message_len,
+				  data_offset, data_len, true);
+	if (ret < 0) {
+		CHECK(1);
+		pr_warn("Encryption failed %d\n", ret);
+		goto out;
+	}
+	if (ret != message_len) {
+		CHECK(1);
+		pr_warn("Encrypted message wrong size %x != %zx\n", ret, message_len);
+		goto out;
+	}
+
+	if (memcmp(buf, ct.data, ct.len) != 0) {
+		CHECK(1);
+		pr_warn("Ciphertext mismatch\n");
+		pr_warn("BUF %*phN\n", ct.len, buf);
+		pr_warn("CT  %*phN\n", ct.len, ct.data);
+		pr_warn("PT  %*phN%*phN\n", conf.len, conf.data, plain.len, plain.data);
+		ret = -EKEYREJECTED;
+		goto out;
+	}
+
+	/* Decrypt the encrypted message. */
+	data_offset = 0;
+	data_len = message_len;
+	ret = crypto_krb5_decrypt(krb5, ci, sg, 1, &data_offset, &data_len);
+	if (ret < 0) {
+		CHECK(1);
+		pr_warn("Decryption failed %d\n", ret);
+		goto out;
+	}
+
+	if (CHECK(data_offset != conf.len) ||
+	    CHECK(data_len != plain.len))
+		goto out;
+
+	if (memcmp(buf, conf.data, conf.len) != 0) {
+		CHECK(1);
+		pr_warn("Confounder mismatch\n");
+		pr_warn("ENC %*phN\n", conf.len, buf);
+		pr_warn("DEC %*phN\n", conf.len, conf.data);
+		ret = -EKEYREJECTED;
+		goto out;
+	}
+
+	if (memcmp(buf + conf.len, plain.data, plain.len) != 0) {
+		CHECK(1);
+		pr_warn("Plaintext mismatch\n");
+		pr_warn("BUF %*phN\n", plain.len, buf + conf.len);
+		pr_warn("PT  %*phN\n", plain.len, plain.data);
+		ret = -EKEYREJECTED;
+		goto out;
+	}
+
+	ret = 0;
+
+out:
+	clear_buf(&ct);
+	clear_buf(&plain);
+	clear_buf(&conf);
+	clear_buf(&keys);
+	clear_buf(&Ki);
+	clear_buf(&Ke);
+	clear_buf(&K0);
+	if (ci)
+		crypto_free_aead(ci);
+	return ret;
+}
+
+/*
+ * Perform a checksum test.
+ */
+static int krb5_test_one_mic(const struct krb5_mic_test *test, void *buf)
+{
+	const struct krb5_enctype *krb5 = crypto_krb5_find_enctype(test->etype);
+	struct crypto_shash *ci = NULL;
+	struct scatterlist sg[1];
+	struct krb5_buffer K0 = {}, Kc = {}, keys = {}, plain = {}, mic = {};
+	size_t offset, len, message_len;
+	int ret;
+
+	if (!krb5)
+		return -EOPNOTSUPP;
+
+	pr_notice("Running %s %s\n", krb5->name, test->name);
+
+	/* Allocate a crypto object and set its key. */
+	if (test->K0) {
+		LOAD_BUF(&K0, test->K0);
+		ci = crypto_krb5_prepare_checksum(krb5, &K0, test->usage, GFP_KERNEL);
+	} else {
+		LOAD_BUF(&Kc, test->Kc);
+
+		ret = krb5->profile->load_checksum_key(krb5, &Kc, &keys, GFP_KERNEL);
+		if (ret < 0)
+			goto out;
+
+		ci = krb5_prepare_checksum(krb5, &Kc, GFP_KERNEL);
+	}
+	if (IS_ERR(ci)) {
+		ret = PTR_ERR(ci);
+		ci = NULL;
+		pr_err("Couldn't alloc shash %s: %d\n", krb5->cksum_name, ret);
+		goto out;
+	}
+
+	/* Load the test data into binary buffers. */
+	LOAD_BUF(&plain, test->plain);
+	LOAD_BUF(&mic, test->mic);
+
+	len = plain.len;
+	message_len = crypto_krb5_how_much_buffer(krb5, KRB5_CHECKSUM_MODE,
+						  len, &offset);
+
+	if (CHECK(message_len != mic.len + plain.len)) {
+		pr_warn("MIC length mismatch %zu != %u\n",
+			message_len, mic.len + plain.len);
+		goto out;
+	}
+
+	memcpy(buf + offset, plain.data, plain.len);
+
+	/* Generate a MIC generation request. */
+	sg_init_one(sg, buf, 1024);
+
+	ret = crypto_krb5_get_mic(krb5, ci, NULL, sg, 1, 1024,
+				  krb5->cksum_len, plain.len);
+	if (ret < 0) {
+		CHECK(1);
+		pr_warn("Get MIC failed %d\n", ret);
+		goto out;
+	}
+	len = ret;
+
+	if (CHECK(len != plain.len + mic.len)) {
+		pr_warn("MIC length mismatch %zu != %u\n", len, plain.len + mic.len);
+		goto out;
+	}
+
+	if (memcmp(buf, mic.data, mic.len) != 0) {
+		CHECK(1);
+		pr_warn("MIC mismatch\n");
+		pr_warn("BUF %*phN\n", mic.len, buf);
+		pr_warn("MIC %*phN\n", mic.len, mic.data);
+		ret = -EKEYREJECTED;
+		goto out;
+	}
+
+	/* Generate a verification request. */
+	offset = 0;
+	ret = crypto_krb5_verify_mic(krb5, ci, NULL, sg, 1, &offset, &len);
+	if (ret < 0) {
+		CHECK(1);
+		pr_warn("Verify MIC failed %d\n", ret);
+		goto out;
+	}
+
+	if (CHECK(offset != mic.len) ||
+	    CHECK(len != plain.len))
+		goto out;
+
+	if (memcmp(buf + offset, plain.data, plain.len) != 0) {
+		CHECK(1);
+		pr_warn("Plaintext mismatch\n");
+		pr_warn("BUF %*phN\n", plain.len, buf + offset);
+		pr_warn("PT  %*phN\n", plain.len, plain.data);
+		ret = -EKEYREJECTED;
+		goto out;
+	}
+
+	ret = 0;
+
+out:
+	clear_buf(&mic);
+	clear_buf(&plain);
+	clear_buf(&keys);
+	clear_buf(&K0);
+	clear_buf(&Kc);
+	if (ci)
+		crypto_free_shash(ci);
+	return ret;
+}
+
+int krb5_selftest(void)
+{
+	void *buf;
+	int ret = 0, i;
+
+	buf = kmalloc(4096, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	pr_notice("\n");
+	pr_notice("Running selftests\n");
+
+	for (i = 0; krb5_prf_tests[i].name; i++) {
+		ret = krb5_test_one_prf(&krb5_prf_tests[i]);
+		if (ret < 0) {
+			if (ret != -EOPNOTSUPP)
+				goto out;
+			pr_notice("Skipping %s\n", krb5_prf_tests[i].name);
+		}
+	}
+
+	for (i = 0; krb5_key_tests[i].name; i++) {
+		ret = krb5_test_one_key(&krb5_key_tests[i]);
+		if (ret < 0) {
+			if (ret != -EOPNOTSUPP)
+				goto out;
+			pr_notice("Skipping %s\n", krb5_key_tests[i].name);
+		}
+	}
+
+	for (i = 0; krb5_enc_tests[i].name; i++) {
+		memset(buf, 0x5a, 4096);
+		ret = krb5_test_one_enc(&krb5_enc_tests[i], buf);
+		if (ret < 0) {
+			if (ret != -EOPNOTSUPP)
+				goto out;
+			pr_notice("Skipping %s\n", krb5_enc_tests[i].name);
+		}
+	}
+
+	for (i = 0; krb5_mic_tests[i].name; i++) {
+		memset(buf, 0x5a, 4096);
+		ret = krb5_test_one_mic(&krb5_mic_tests[i], buf);
+		if (ret < 0) {
+			if (ret != -EOPNOTSUPP)
+				goto out;
+			pr_notice("Skipping %s\n", krb5_mic_tests[i].name);
+		}
+	}
+
+	ret = 0;
+out:
+	pr_notice("Selftests %s\n", ret == 0 ? "succeeded" : "failed");
+	kfree(buf);
+	return ret;
+}
diff --git a/crypto/krb5/selftest_data.c b/crypto/krb5/selftest_data.c
new file mode 100644
index 000000000000..bef5b8b342ae
--- /dev/null
+++ b/crypto/krb5/selftest_data.c
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Data for Kerberos library self-testing
+ *
+ * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells at redhat.com)
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "internal.h"
+
+/*
+ * Pseudo-random function tests.
+ */
+const struct krb5_prf_test krb5_prf_tests[] = {
+	{/* END */}
+};
+
+/*
+ * Key derivation tests.
+ */
+const struct krb5_key_test krb5_key_tests[] = {
+	{/* END */}
+};
+
+/*
+ * Encryption tests.
+ */
+const struct krb5_enc_test krb5_enc_tests[] = {
+	{/* END */}
+};
+
+/*
+ * Checksum generation tests.
+ */
+const struct krb5_mic_test krb5_mic_tests[] = {
+	{/* END */}
+};



From dhowells at redhat.com  Fri Jan 17 10:35:27 2025
From: dhowells at redhat.com (David Howells)
Date: Fri, 17 Jan 2025 18:35:27 +0000
Subject: [RFC PATCH 18/24] crypto/krb5: Add the AES self-testing data from rfc8009
In-Reply-To: <20250117183538.881618-1-dhowells@redhat.com>
References: <20250117183538.881618-1-dhowells@redhat.com>
Message-ID: <20250117183538.881618-19-dhowells@redhat.com>

Add the self-testing data from rfc8009 to test AES + HMAC-SHA2.

Signed-off-by: David Howells <dhowells at redhat.com>
cc: Herbert Xu <herbert at gondor.apana.org.au>
cc: "David S. Miller" <davem at davemloft.net>
cc: Chuck Lever <chuck.lever at oracle.com>
cc: Marc Dionne <marc.dionne at auristor.com>
cc: Eric Dumazet <edumazet at google.com>
cc: Jakub Kicinski <kuba at kernel.org>
cc: Paolo Abeni <pabeni at redhat.com>
cc: Simon Horman <horms at kernel.org>
cc: linux-afs at lists.infradead.org
cc: linux-nfs at vger.kernel.org
cc: linux-crypto at vger.kernel.org
cc: netdev at vger.kernel.org
---
 crypto/krb5/selftest_data.c | 118 ++++++++++++++++++++++++++++++++++++
 1 file changed, 118 insertions(+)

diff --git a/crypto/krb5/selftest_data.c b/crypto/krb5/selftest_data.c
index bef5b8b342ae..3a5563d57280 100644
--- a/crypto/krb5/selftest_data.c
+++ b/crypto/krb5/selftest_data.c
@@ -13,6 +13,22 @@
  * Pseudo-random function tests.
  */
 const struct krb5_prf_test krb5_prf_tests[] = {
+	/* rfc8009 Appendix A */
+	{
+		.etype	= KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128,
+		.name	= "prf",
+		.key	= "3705D96080C17728A0E800EAB6E0D23C",
+		.octet	= "74657374",
+		.prf	= "9D188616F63852FE86915BB840B4A886FF3E6BB0F819B49B893393D393854295",
+	}, {
+		.etype	= KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192,
+		.name	= "prf",
+		.key	= "6D404D37FAF79F9DF0D33568D320669800EB4836472EA8A026D16B7182460C52",
+		.octet	= "74657374",
+		.prf	=
+		"9801F69A368C2BF675E59521E177D9A07F67EFE1CFDE8D3C8D6F6A0256E3B17D"
+		"B3C1B62AD1B8553360D17367EB1514D2",
+	},
 	{/* END */}
 };
 
@@ -20,6 +36,28 @@ const struct krb5_prf_test krb5_prf_tests[] = {
  * Key derivation tests.
  */
 const struct krb5_key_test krb5_key_tests[] = {
+	/* rfc8009 Appendix A */
+	{
+		.etype	= KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128,
+		.name	= "key",
+		.key	= "3705D96080C17728A0E800EAB6E0D23C",
+		.Kc.use	= 0x00000002,
+		.Kc.key	= "B31A018A48F54776F403E9A396325DC3",
+		.Ke.use	= 0x00000002,
+		.Ke.key	= "9B197DD1E8C5609D6E67C3E37C62C72E",
+		.Ki.use	= 0x00000002,
+		.Ki.key	= "9FDA0E56AB2D85E1569A688696C26A6C",
+	}, {
+		.etype	= KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192,
+		.name	= "key",
+		.key	= "6D404D37FAF79F9DF0D33568D320669800EB4836472EA8A026D16B7182460C52",
+		.Kc.use	= 0x00000002,
+		.Kc.key	= "EF5718BE86CC84963D8BBB5031E9F5C4BA41F28FAF69E73D",
+		.Ke.use	= 0x00000002,
+		.Ke.key	= "56AB22BEE63D82D7BC5227F6773F8EA7A5EB1C825160C38312980C442E5C7E49",
+		.Ki.use	= 0x00000002,
+		.Ki.key	= "69B16514E3CD8E56B82010D5C73012B622C4D00FFC23ED1F",
+	},
 	{/* END */}
 };
 
@@ -27,6 +65,72 @@ const struct krb5_key_test krb5_key_tests[] = {
  * Encryption tests.
  */
 const struct krb5_enc_test krb5_enc_tests[] = {
+	/* rfc8009 Appendix A */
+	{
+		.etype	= KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128,
+		.name	= "enc no plain",
+		.plain	= "",
+		.conf	= "7E5895EAF2672435BAD817F545A37148",
+		.Ke	= "9B197DD1E8C5609D6E67C3E37C62C72E",
+		.Ki	= "9FDA0E56AB2D85E1569A688696C26A6C",
+		.ct	= "EF85FB890BB8472F4DAB20394DCA781DAD877EDA39D50C870C0D5A0A8E48C718",
+	}, {
+		.etype	= KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128,
+		.name	= "enc plain<block",
+		.plain	= "000102030405",
+		.conf	= "7BCA285E2FD4130FB55B1A5C83BC5B24",
+		.Ke	= "9B197DD1E8C5609D6E67C3E37C62C72E",
+		.Ki	= "9FDA0E56AB2D85E1569A688696C26A6C",
+		.ct	= "84D7F30754ED987BAB0BF3506BEB09CFB55402CEF7E6877CE99E247E52D16ED4421DFDF8976C",
+	}, {
+		.etype	= KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128,
+		.name	= "enc plain==block",
+		.plain	= "000102030405060708090A0B0C0D0E0F",
+		.conf	= "56AB21713FF62C0A1457200F6FA9948F",
+		.Ke	= "9B197DD1E8C5609D6E67C3E37C62C72E",
+		.Ki	= "9FDA0E56AB2D85E1569A688696C26A6C",
+		.ct	= "3517D640F50DDC8AD3628722B3569D2AE07493FA8263254080EA65C1008E8FC295FB4852E7D83E1E7C48C37EEBE6B0D3",
+	}, {
+		.etype	= KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128,
+		.name	= "enc plain>block",
+		.plain	= "000102030405060708090A0B0C0D0E0F1011121314",
+		.conf	= "A7A4E29A4728CE10664FB64E49AD3FAC",
+		.Ke	= "9B197DD1E8C5609D6E67C3E37C62C72E",
+		.Ki	= "9FDA0E56AB2D85E1569A688696C26A6C",
+		.ct	= "720F73B18D9859CD6CCB4346115CD336C70F58EDC0C4437C5573544C31C813BCE1E6D072C186B39A413C2F92CA9B8334A287FFCBFC",
+	}, {
+		.etype	= KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192,
+		.name	= "enc no plain",
+		.plain	= "",
+		.conf	= "F764E9FA15C276478B2C7D0C4E5F58E4",
+		.Ke	= "56AB22BEE63D82D7BC5227F6773F8EA7A5EB1C825160C38312980C442E5C7E49",
+		.Ki	= "69B16514E3CD8E56B82010D5C73012B622C4D00FFC23ED1F",
+		.ct	= "41F53FA5BFE7026D91FAF9BE959195A058707273A96A40F0A01960621AC612748B9BBFBE7EB4CE3C",
+	}, {
+		.etype	= KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192,
+		.name	= "enc plain<block",
+		.plain	= "000102030405",
+		.conf	= "B80D3251C1F6471494256FFE712D0B9A",
+		.Ke	= "56AB22BEE63D82D7BC5227F6773F8EA7A5EB1C825160C38312980C442E5C7E49",
+		.Ki	= "69B16514E3CD8E56B82010D5C73012B622C4D00FFC23ED1F",
+		.ct	= "4ED7B37C2BCAC8F74F23C1CF07E62BC7B75FB3F637B9F559C7F664F69EAB7B6092237526EA0D1F61CB20D69D10F2",
+	}, {
+		.etype	= KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192,
+		.name	= "enc plain==block",
+		.plain	= "000102030405060708090A0B0C0D0E0F",
+		.conf	= "53BF8A0D105265D4E276428624CE5E63",
+		.Ke	= "56AB22BEE63D82D7BC5227F6773F8EA7A5EB1C825160C38312980C442E5C7E49",
+		.Ki	= "69B16514E3CD8E56B82010D5C73012B622C4D00FFC23ED1F",
+		.ct	= "BC47FFEC7998EB91E8115CF8D19DAC4BBBE2E163E87DD37F49BECA92027764F68CF51F14D798C2273F35DF574D1F932E40C4FF255B36A266",
+	}, {
+		.etype	= KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192,
+		.name	= "enc plain>block",
+		.plain	= "000102030405060708090A0B0C0D0E0F1011121314",
+		.conf	= "763E65367E864F02F55153C7E3B58AF1",
+		.Ke	= "56AB22BEE63D82D7BC5227F6773F8EA7A5EB1C825160C38312980C442E5C7E49",
+		.Ki	= "69B16514E3CD8E56B82010D5C73012B622C4D00FFC23ED1F",
+		.ct	= "40013E2DF58E8751957D2878BCD2D6FE101CCFD556CB1EAE79DB3C3EE86429F2B2A602AC86FEF6ECB647D6295FAE077A1FEB517508D2C16B4192E01F62",
+	},
 	{/* END */}
 };
 
@@ -34,5 +138,19 @@ const struct krb5_enc_test krb5_enc_tests[] = {
  * Checksum generation tests.
  */
 const struct krb5_mic_test krb5_mic_tests[] = {
+	/* rfc8009 Appendix A */
+	{
+		.etype	= KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128,
+		.name	= "mic",
+		.plain	= "000102030405060708090A0B0C0D0E0F1011121314",
+		.Kc	= "B31A018A48F54776F403E9A396325DC3",
+		.mic	= "D78367186643D67B411CBA9139FC1DEE",
+	}, {
+		.etype	= KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192,
+		.name	= "mic",
+		.plain	= "000102030405060708090A0B0C0D0E0F1011121314",
+		.Kc	= "EF5718BE86CC84963D8BBB5031E9F5C4BA41F28FAF69E73D",
+		.mic	= "45EE791567EEFCA37F4AC1E0222DE80D43C3BFA06699672A",
+	},
 	{/* END */}
 };



From dhowells at redhat.com  Fri Jan 17 10:35:28 2025
From: dhowells at redhat.com (David Howells)
Date: Fri, 17 Jan 2025 18:35:28 +0000
Subject: [RFC PATCH 19/24] crypto/krb5: Implement the Camellia enctypes from rfc6803
In-Reply-To: <20250117183538.881618-1-dhowells@redhat.com>
References: <20250117183538.881618-1-dhowells@redhat.com>
Message-ID: <20250117183538.881618-20-dhowells@redhat.com>

Implement the camellia128-cts-cmac and camellia256-cts-cmac enctypes from
rfc6803.

Note that the test vectors in rfc6803 for encryption are incomplete,
lacking the key usage number needed to derive Ke and Ki, and there are
errata for this:

	https://www.rfc-editor.org/errata_search.php?rfc=6803

Signed-off-by: David Howells <dhowells at redhat.com>
cc: Herbert Xu <herbert at gondor.apana.org.au>
cc: "David S. Miller" <davem at davemloft.net>
cc: Chuck Lever <chuck.lever at oracle.com>
cc: Marc Dionne <marc.dionne at auristor.com>
cc: Eric Dumazet <edumazet at google.com>
cc: Jakub Kicinski <kuba at kernel.org>
cc: Paolo Abeni <pabeni at redhat.com>
cc: Simon Horman <horms at kernel.org>
cc: linux-afs at lists.infradead.org
cc: linux-nfs at vger.kernel.org
cc: linux-crypto at vger.kernel.org
cc: netdev at vger.kernel.org
---
 crypto/krb5/Kconfig            |   2 +
 crypto/krb5/Makefile           |   1 +
 crypto/krb5/internal.h         |   6 +
 crypto/krb5/krb5_api.c         |   2 +
 crypto/krb5/rfc6803_camellia.c | 237 +++++++++++++++++++++++++++++++++
 crypto/krb5/selftest_data.c    | 135 +++++++++++++++++++
 include/crypto/krb5.h          |   4 +
 7 files changed, 387 insertions(+)
 create mode 100644 crypto/krb5/rfc6803_camellia.c

diff --git a/crypto/krb5/Kconfig b/crypto/krb5/Kconfig
index 82d7e3b55878..4d0476e13f3c 100644
--- a/crypto/krb5/Kconfig
+++ b/crypto/krb5/Kconfig
@@ -6,12 +6,14 @@ config CRYPTO_KRB5
 	select CRYPTO_SKCIPHER
 	select CRYPTO_HASH_INFO
 	select CRYPTO_HMAC
+	select CRYPTO_CMAC
 	select CRYPTO_SHA1
 	select CRYPTO_SHA256
 	select CRYPTO_SHA512
 	select CRYPTO_CBC
 	select CRYPTO_CTS
 	select CRYPTO_AES
+	select CRYPTO_CAMELLIA
 	help
 	  Provide a library for provision of Kerberos-5-based crypto.  This is
 	  intended for network filesystems to use.
diff --git a/crypto/krb5/Makefile b/crypto/krb5/Makefile
index 1ad327227744..d38890c0b247 100644
--- a/crypto/krb5/Makefile
+++ b/crypto/krb5/Makefile
@@ -8,6 +8,7 @@ krb5-y += \
 	krb5_api.o \
 	rfc3961_simplified.o \
 	rfc3962_aes.o \
+	rfc6803_camellia.o \
 	rfc8009_aes2.o
 
 krb5-$(CONFIG_CRYPTO_KRB5_SELFTESTS) += \
diff --git a/crypto/krb5/internal.h b/crypto/krb5/internal.h
index a40f120b4954..0a7ecd800416 100644
--- a/crypto/krb5/internal.h
+++ b/crypto/krb5/internal.h
@@ -217,6 +217,12 @@ int rfc3961_verify_mic(const struct krb5_enctype *krb5,
 extern const struct krb5_enctype krb5_aes128_cts_hmac_sha1_96;
 extern const struct krb5_enctype krb5_aes256_cts_hmac_sha1_96;
 
+/*
+ * rfc6803_camellia.c
+ */
+extern const struct krb5_enctype krb5_camellia128_cts_cmac;
+extern const struct krb5_enctype krb5_camellia256_cts_cmac;
+
 /*
  * rfc8009_aes2.c
  */
diff --git a/crypto/krb5/krb5_api.c b/crypto/krb5/krb5_api.c
index 8c2949ac9c07..23026d4206c8 100644
--- a/crypto/krb5/krb5_api.c
+++ b/crypto/krb5/krb5_api.c
@@ -21,6 +21,8 @@ static const struct krb5_enctype *const krb5_supported_enctypes[] = {
 	&krb5_aes256_cts_hmac_sha1_96,
 	&krb5_aes128_cts_hmac_sha256_128,
 	&krb5_aes256_cts_hmac_sha384_192,
+	&krb5_camellia128_cts_cmac,
+	&krb5_camellia256_cts_cmac,
 };
 
 /**
diff --git a/crypto/krb5/rfc6803_camellia.c b/crypto/krb5/rfc6803_camellia.c
new file mode 100644
index 000000000000..c2895d2d60ed
--- /dev/null
+++ b/crypto/krb5/rfc6803_camellia.c
@@ -0,0 +1,237 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* rfc6803 Camellia Encryption for Kerberos 5
+ *
+ * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells at redhat.com)
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/slab.h>
+#include "internal.h"
+
+/*
+ * Calculate the key derivation function KDF-FEEDBACK_CMAC(key, constant)
+ *
+ *	n = ceiling(k / 128)
+ *	K(0) = zeros
+ *	K(i) = CMAC(key, K(i-1) | i | constant | 0x00 | k)
+ *	DR(key, constant) = k-truncate(K(1) | K(2) | ... | K(n))
+ *	KDF-FEEDBACK-CMAC(key, constant) = random-to-key(DR(key, constant))
+ *
+ *	[rfc6803 sec 3]
+ */
+static int rfc6803_calc_KDF_FEEDBACK_CMAC(const struct krb5_enctype *krb5,
+					  const struct krb5_buffer *key,
+					  const struct krb5_buffer *constant,
+					  struct krb5_buffer *result,
+					  gfp_t gfp)
+{
+	struct crypto_shash *shash;
+	struct krb5_buffer K, data;
+	struct shash_desc *desc;
+	__be32 tmp;
+	size_t bsize, offset, seg;
+	void *buffer;
+	u32 i = 0, k = result->len * 8;
+	u8 *p;
+	int ret = -ENOMEM;
+
+	shash = crypto_alloc_shash(krb5->cksum_name, 0, 0);
+	if (IS_ERR(shash))
+		return (PTR_ERR(shash) == -ENOENT) ? -ENOPKG : PTR_ERR(shash);
+	ret = crypto_shash_setkey(shash, key->data, key->len);
+	if (ret < 0)
+		goto error_shash;
+
+	ret = -ENOMEM;
+	K.len = crypto_shash_digestsize(shash);
+	data.len = K.len + 4 + constant->len + 1 + 4;
+	bsize = krb5_shash_size(shash) +
+		krb5_digest_size(shash) +
+		crypto_roundup(K.len) +
+		crypto_roundup(data.len);
+	buffer = kzalloc(bsize, GFP_NOFS);
+	if (!buffer)
+		goto error_shash;
+
+	desc = buffer;
+	desc->tfm = shash;
+
+	K.data = buffer +
+		krb5_shash_size(shash) +
+		krb5_digest_size(shash);
+	data.data = buffer +
+		krb5_shash_size(shash) +
+		krb5_digest_size(shash) +
+		crypto_roundup(K.len);
+
+	p = data.data + K.len + 4;
+	memcpy(p, constant->data, constant->len);
+	p += constant->len;
+	*p++ = 0x00;
+	tmp = htonl(k);
+	memcpy(p, &tmp, 4);
+	p += 4;
+
+	ret = -EINVAL;
+	if (WARN_ON(p - (u8 *)data.data != data.len))
+		goto error;
+
+	offset = 0;
+	do {
+		i++;
+		p = data.data;
+		memcpy(p, K.data, K.len);
+		p += K.len;
+		*(__be32 *)p = htonl(i);
+
+		ret = crypto_shash_init(desc);
+		if (ret < 0)
+			goto error;
+		ret = crypto_shash_finup(desc, data.data, data.len, K.data);
+		if (ret < 0)
+			goto error;
+
+		seg = min_t(size_t, result->len - offset, K.len);
+		memcpy(result->data + offset, K.data, seg);
+		offset += seg;
+	} while (offset < result->len);
+
+error:
+	kfree_sensitive(buffer);
+error_shash:
+	crypto_free_shash(shash);
+	return ret;
+}
+
+/*
+ * Calculate the pseudo-random function, PRF().
+ *
+ *	Kp = KDF-FEEDBACK-CMAC(protocol-key, "prf")
+ *	PRF = CMAC(Kp, octet-string)
+ *      [rfc6803 sec 6]
+ */
+static int rfc6803_calc_PRF(const struct krb5_enctype *krb5,
+			    const struct krb5_buffer *protocol_key,
+			    const struct krb5_buffer *octet_string,
+			    struct krb5_buffer *result,
+			    gfp_t gfp)
+{
+	static const struct krb5_buffer prfconstant = { 3, "prf" };
+	struct crypto_shash *shash;
+	struct krb5_buffer Kp;
+	struct shash_desc *desc;
+	size_t bsize;
+	void *buffer;
+	int ret;
+
+	Kp.len = krb5->prf_len;
+
+	shash = crypto_alloc_shash(krb5->cksum_name, 0, 0);
+	if (IS_ERR(shash))
+		return (PTR_ERR(shash) == -ENOENT) ? -ENOPKG : PTR_ERR(shash);
+
+	ret = -EINVAL;
+	if (result->len != crypto_shash_digestsize(shash))
+		goto out_shash;
+
+	ret = -ENOMEM;
+	bsize = krb5_shash_size(shash) +
+		krb5_digest_size(shash) +
+		crypto_roundup(Kp.len);
+	buffer = kzalloc(bsize, GFP_NOFS);
+	if (!buffer)
+		goto out_shash;
+
+	Kp.data = buffer +
+		krb5_shash_size(shash) +
+		krb5_digest_size(shash);
+
+	ret = rfc6803_calc_KDF_FEEDBACK_CMAC(krb5, protocol_key, &prfconstant,
+					     &Kp, gfp);
+	if (ret < 0)
+		goto out;
+
+	ret = crypto_shash_setkey(shash, Kp.data, Kp.len);
+	if (ret < 0)
+		goto out;
+
+	desc = buffer;
+	desc->tfm = shash;
+	ret = crypto_shash_init(desc);
+	if (ret < 0)
+		goto out;
+
+	ret = crypto_shash_finup(desc, octet_string->data, octet_string->len, result->data);
+	if (ret < 0)
+		goto out;
+
+out:
+	kfree_sensitive(buffer);
+out_shash:
+	crypto_free_shash(shash);
+	return ret;
+}
+
+
+static const struct krb5_crypto_profile rfc6803_crypto_profile = {
+	.calc_PRF		= rfc6803_calc_PRF,
+	.calc_Kc		= rfc6803_calc_KDF_FEEDBACK_CMAC,
+	.calc_Ke		= rfc6803_calc_KDF_FEEDBACK_CMAC,
+	.calc_Ki		= rfc6803_calc_KDF_FEEDBACK_CMAC,
+	.derive_encrypt_keys	= krb5enc_derive_encrypt_keys,
+	.load_encrypt_keys	= krb5enc_load_encrypt_keys,
+	.derive_checksum_key	= rfc3961_derive_checksum_key,
+	.load_checksum_key	= rfc3961_load_checksum_key,
+	.encrypt		= krb5_aead_encrypt,
+	.decrypt		= krb5_aead_decrypt,
+	.get_mic		= rfc3961_get_mic,
+	.verify_mic		= rfc3961_verify_mic,
+};
+
+const struct krb5_enctype krb5_camellia128_cts_cmac = {
+	.etype		= KRB5_ENCTYPE_CAMELLIA128_CTS_CMAC,
+	.ctype		= KRB5_CKSUMTYPE_CMAC_CAMELLIA128,
+	.name		= "camellia128-cts-cmac",
+	.encrypt_name	= "krb5enc(cmac(camellia),cts(cbc(camellia)))",
+	.cksum_name	= "cmac(camellia)",
+	.hash_name	= NULL,
+	.derivation_enc	= "cts(cbc(camellia))",
+	.key_bytes	= 16,
+	.key_len	= 16,
+	.Kc_len		= 16,
+	.Ke_len		= 16,
+	.Ki_len		= 16,
+	.block_len	= 16,
+	.conf_len	= 16,
+	.cksum_len	= 16,
+	.hash_len	= 16,
+	.prf_len	= 16,
+	.keyed_cksum	= true,
+	.random_to_key	= NULL, /* Identity */
+	.profile	= &rfc6803_crypto_profile,
+};
+
+const struct krb5_enctype krb5_camellia256_cts_cmac = {
+	.etype		= KRB5_ENCTYPE_CAMELLIA256_CTS_CMAC,
+	.ctype		= KRB5_CKSUMTYPE_CMAC_CAMELLIA256,
+	.name		= "camellia256-cts-cmac",
+	.encrypt_name	= "krb5enc(cmac(camellia),cts(cbc(camellia)))",
+	.cksum_name	= "cmac(camellia)",
+	.hash_name	= NULL,
+	.derivation_enc	= "cts(cbc(camellia))",
+	.key_bytes	= 32,
+	.key_len	= 32,
+	.Kc_len		= 32,
+	.Ke_len		= 32,
+	.Ki_len		= 32,
+	.block_len	= 16,
+	.conf_len	= 16,
+	.cksum_len	= 16,
+	.hash_len	= 16,
+	.prf_len	= 16,
+	.keyed_cksum	= true,
+	.random_to_key	= NULL, /* Identity */
+	.profile	= &rfc6803_crypto_profile,
+};
diff --git a/crypto/krb5/selftest_data.c b/crypto/krb5/selftest_data.c
index 3a5563d57280..24447ee8bf07 100644
--- a/crypto/krb5/selftest_data.c
+++ b/crypto/krb5/selftest_data.c
@@ -58,6 +58,29 @@ const struct krb5_key_test krb5_key_tests[] = {
 		.Ki.use	= 0x00000002,
 		.Ki.key	= "69B16514E3CD8E56B82010D5C73012B622C4D00FFC23ED1F",
 	},
+	/* rfc6803 sec 10 */
+	{
+		.etype	= KRB5_ENCTYPE_CAMELLIA128_CTS_CMAC,
+		.name	= "key",
+		.key	= "57D0297298FFD9D35DE5A47FB4BDE24B",
+		.Kc.use	= 0x00000002,
+		.Kc.key	= "D155775A209D05F02B38D42A389E5A56",
+		.Ke.use	= 0x00000002,
+		.Ke.key	= "64DF83F85A532F17577D8C37035796AB",
+		.Ki.use	= 0x00000002,
+		.Ki.key	= "3E4FBDF30FB8259C425CB6C96F1F4635",
+	},
+	{
+		.etype	= KRB5_ENCTYPE_CAMELLIA256_CTS_CMAC,
+		.name	= "key",
+		.key	= "B9D6828B2056B7BE656D88A123B1FAC68214AC2B727ECF5F69AFE0C4DF2A6D2C",
+		.Kc.use	= 0x00000002,
+		.Kc.key	= "E467F9A9552BC7D3155A6220AF9C19220EEED4FF78B0D1E6A1544991461A9E50",
+		.Ke.use	= 0x00000002,
+		.Ke.key	= "412AEFC362A7285FC3966C6A5181E7605AE675235B6D549FBFC9AB6630A4C604",
+		.Ki.use	= 0x00000002,
+		.Ki.key	= "FA624FA0E523993FA388AEFDC67E67EBCD8C08E8A0246B1D73B0D1DD9FC582B0",
+	},
 	{/* END */}
 };
 
@@ -131,6 +154,88 @@ const struct krb5_enc_test krb5_enc_tests[] = {
 		.Ki	= "69B16514E3CD8E56B82010D5C73012B622C4D00FFC23ED1F",
 		.ct	= "40013E2DF58E8751957D2878BCD2D6FE101CCFD556CB1EAE79DB3C3EE86429F2B2A602AC86FEF6ECB647D6295FAE077A1FEB517508D2C16B4192E01F62",
 	},
+	/* rfc6803 sec 10 */
+	{
+		.etype	= KRB5_ENCTYPE_CAMELLIA128_CTS_CMAC,
+		.name	= "enc no plain",
+		.plain	= "",
+		.conf	= "B69822A19A6B09C0EBC8557D1F1B6C0A",
+		.K0	= "1DC46A8D763F4F93742BCBA3387576C3",
+		.usage	= 0,
+		.ct	= "C466F1871069921EDB7C6FDE244A52DB0BA10EDC197BDB8006658CA3CCCE6EB8",
+	}, {
+		.etype	= KRB5_ENCTYPE_CAMELLIA128_CTS_CMAC,
+		.name	= "enc 1 plain",
+		.plain	= "'1",
+		.conf	= "6F2FC3C2A166FD8898967A83DE9596D9",
+		.K0	= "5027BC231D0F3A9D23333F1CA6FDBE7C",
+		.usage	= 1,
+		.ct	= "842D21FD950311C0DD464A3F4BE8D6DA88A56D559C9B47D3F9A85067AF661559B8",
+	}, {
+		.etype	= KRB5_ENCTYPE_CAMELLIA128_CTS_CMAC,
+		.name	= "enc 9 plain",
+		.plain	= "'9 bytesss",
+		.conf	= "A5B4A71E077AEEF93C8763C18FDB1F10",
+		.K0	= "A1BB61E805F9BA6DDE8FDBDDC05CDEA0",
+		.usage	= 2,
+		.ct	= "619FF072E36286FF0A28DEB3A352EC0D0EDF5C5160D663C901758CCF9D1ED33D71DB8F23AABF8348A0",
+	}, {
+		.etype	= KRB5_ENCTYPE_CAMELLIA128_CTS_CMAC,
+		.name	= "enc 13 plain",
+		.plain	= "'13 bytes byte",
+		.conf	= "19FEE40D810C524B5B22F01874C693DA",
+		.K0	= "2CA27A5FAF5532244506434E1CEF6676",
+		.usage	= 3,
+		.ct	= "B8ECA3167AE6315512E59F98A7C500205E5F63FF3BB389AF1C41A21D640D8615C9ED3FBEB05AB6ACB67689B5EA",
+	}, {
+		.etype	= KRB5_ENCTYPE_CAMELLIA128_CTS_CMAC,
+		.name	= "enc 30 plain",
+		.plain	= "'30 bytes bytes bytes bytes byt",
+		.conf	= "CA7A7AB4BE192DABD603506DB19C39E2",
+		.K0	= "7824F8C16F83FF354C6BF7515B973F43",
+		.usage	= 4,
+		.ct	= "A26A3905A4FFD5816B7B1E27380D08090C8EC1F304496E1ABDCD2BDCD1DFFC660989E117A713DDBB57A4146C1587CBA4356665591D2240282F5842B105A5",
+	}, {
+		.etype	= KRB5_ENCTYPE_CAMELLIA256_CTS_CMAC,
+		.name	= "enc no plain",
+		.plain	= "",
+		.conf	= "3CBBD2B45917941067F96599BB98926C",
+		.K0	= "B61C86CC4E5D2757545AD423399FB7031ECAB913CBB900BD7A3C6DD8BF92015B",
+		.usage	= 0,
+		.ct	= "03886D03310B47A6D8F06D7B94D1DD837ECCE315EF652AFF620859D94A259266",
+	}, {
+		.etype	= KRB5_ENCTYPE_CAMELLIA256_CTS_CMAC,
+		.name	= "enc 1 plain",
+		.plain	= "'1",
+		.conf	= "DEF487FCEBE6DE6346D4DA4521BBA2D2",
+		.K0	= "1B97FE0A190E2021EB30753E1B6E1E77B0754B1D684610355864104963463833",
+		.usage	= 1,
+		.ct	= "2C9C1570133C99BF6A34BC1B0212002FD194338749DB4135497A347CFCD9D18A12",
+	}, {
+		.etype	= KRB5_ENCTYPE_CAMELLIA256_CTS_CMAC,
+		.name	= "enc 9 plain",
+		.plain	= "'9 bytesss",
+		.conf	= "AD4FF904D34E555384B14100FC465F88",
+		.K0	= "32164C5B434D1D1538E4CFD9BE8040FE8C4AC7ACC4B93D3314D2133668147A05",
+		.usage	= 2,
+		.ct	= "9C6DE75F812DE7ED0D28B2963557A115640998275B0AF5152709913FF52A2A9C8E63B872F92E64C839",
+	}, {
+		.etype	= KRB5_ENCTYPE_CAMELLIA256_CTS_CMAC,
+		.name	= "enc 13 plain",
+		.plain	= "'13 bytes byte",
+		.conf	= "CF9BCA6DF1144E0C0AF9B8F34C90D514",
+		.K0	= "B038B132CD8E06612267FAB7170066D88AECCBA0B744BFC60DC89BCA182D0715",
+		.usage	= 3,
+		.ct	= "EEEC85A9813CDC536772AB9B42DEFC5706F726E975DDE05A87EB5406EA324CA185C9986B42AABE794B84821BEE",
+	}, {
+		.etype	= KRB5_ENCTYPE_CAMELLIA256_CTS_CMAC,
+		.name	= "enc 30 plain",
+		.plain	= "'30 bytes bytes bytes bytes byt",
+		.conf	= "644DEF38DA35007275878D216855E228",
+		.K0	= "CCFCD349BF4C6677E86E4B02B8EAB924A546AC731CF9BF6989B996E7D6BFBBA7",
+		.usage	= 4,
+		.ct	= "0E44680985855F2D1F1812529CA83BFD8E349DE6FD9ADA0BAAA048D68E265FEBF34AD1255A344999AD37146887A6C6845731AC7F46376A0504CD06571474",
+	},
 	{/* END */}
 };
 
@@ -152,5 +257,35 @@ const struct krb5_mic_test krb5_mic_tests[] = {
 		.Kc	= "EF5718BE86CC84963D8BBB5031E9F5C4BA41F28FAF69E73D",
 		.mic	= "45EE791567EEFCA37F4AC1E0222DE80D43C3BFA06699672A",
 	},
+	/* rfc6803 sec 10 */
+	{
+		.etype	= KRB5_ENCTYPE_CAMELLIA128_CTS_CMAC,
+		.name	= "mic abc",
+		.plain	= "'abcdefghijk",
+		.K0	= "1DC46A8D763F4F93742BCBA3387576C3",
+		.usage	= 7,
+		.mic	= "1178E6C5C47A8C1AE0C4B9C7D4EB7B6B",
+	}, {
+		.etype	= KRB5_ENCTYPE_CAMELLIA128_CTS_CMAC,
+		.name	= "mic ABC",
+		.plain	= "'ABCDEFGHIJKLMNOPQRSTUVWXYZ",
+		.K0	= "5027BC231D0F3A9D23333F1CA6FDBE7C",
+		.usage	= 8,
+		.mic	= "D1B34F7004A731F23A0C00BF6C3F753A",
+	}, {
+		.etype	= KRB5_ENCTYPE_CAMELLIA256_CTS_CMAC,
+		.name	= "mic 123",
+		.plain	= "'123456789",
+		.K0	= "B61C86CC4E5D2757545AD423399FB7031ECAB913CBB900BD7A3C6DD8BF92015B",
+		.usage	= 9,
+		.mic	= "87A12CFD2B96214810F01C826E7744B1",
+	}, {
+		.etype	= KRB5_ENCTYPE_CAMELLIA256_CTS_CMAC,
+		.name	= "mic !@#",
+		.plain	= "'!@#$%^&*()!@#$%^&*()!@#$%^&*()",
+		.K0	= "32164C5B434D1D1538E4CFD9BE8040FE8C4AC7ACC4B93D3314D2133668147A05",
+		.usage	= 10,
+		.mic	= "3FA0B42355E52B189187294AA252AB64",
+	},
 	{/* END */}
 };
diff --git a/include/crypto/krb5.h b/include/crypto/krb5.h
index 6921785688bf..b337494ccb31 100644
--- a/include/crypto/krb5.h
+++ b/include/crypto/krb5.h
@@ -35,6 +35,8 @@ struct scatterlist;
 #define KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192	0x0014
 #define KRB5_ENCTYPE_ARCFOUR_HMAC		0x0017
 #define KRB5_ENCTYPE_ARCFOUR_HMAC_EXP		0x0018
+#define KRB5_ENCTYPE_CAMELLIA128_CTS_CMAC	0x0019
+#define KRB5_ENCTYPE_CAMELLIA256_CTS_CMAC	0x001a
 #define KRB5_ENCTYPE_UNKNOWN			0x01ff
 
 #define KRB5_CKSUMTYPE_CRC32			0x0001
@@ -47,6 +49,8 @@ struct scatterlist;
 #define KRB5_CKSUMTYPE_HMAC_SHA1_DES3		0x000c
 #define KRB5_CKSUMTYPE_HMAC_SHA1_96_AES128	0x000f
 #define KRB5_CKSUMTYPE_HMAC_SHA1_96_AES256	0x0010
+#define KRB5_CKSUMTYPE_CMAC_CAMELLIA128		0x0011
+#define KRB5_CKSUMTYPE_CMAC_CAMELLIA256		0x0012
 #define KRB5_CKSUMTYPE_HMAC_SHA256_128_AES128	0x0013
 #define KRB5_CKSUMTYPE_HMAC_SHA384_192_AES256	0x0014
 #define KRB5_CKSUMTYPE_HMAC_MD5_ARCFOUR		-138 /* Microsoft md5 hmac cksumtype */



From dhowells at redhat.com  Fri Jan 17 10:35:29 2025
From: dhowells at redhat.com (David Howells)
Date: Fri, 17 Jan 2025 18:35:29 +0000
Subject: [RFC PATCH 20/24] rxrpc: Add the security index for yfs-rxgk
In-Reply-To: <20250117183538.881618-1-dhowells@redhat.com>
References: <20250117183538.881618-1-dhowells@redhat.com>
Message-ID: <20250117183538.881618-21-dhowells@redhat.com>

Add the security index and abort codes for the YFS variant of rxgk.

Signed-off-by: David Howells <dhowells at redhat.com>
---
 fs/afs/misc.c              | 13 +++++++++++++
 include/uapi/linux/rxrpc.h | 17 +++++++++++++++++
 2 files changed, 30 insertions(+)

diff --git a/fs/afs/misc.c b/fs/afs/misc.c
index b8180bf2281f..57f779804d50 100644
--- a/fs/afs/misc.c
+++ b/fs/afs/misc.c
@@ -103,6 +103,19 @@ int afs_abort_to_error(u32 abort_code)
 	case RXKADDATALEN:	return -EKEYREJECTED;
 	case RXKADILLEGALLEVEL:	return -EKEYREJECTED;
 
+	case RXGK_INCONSISTENCY:	return -EPROTO;
+	case RXGK_PACKETSHORT:		return -EPROTO;
+	case RXGK_BADCHALLENGE:		return -EPROTO;
+	case RXGK_BADETYPE:		return -ENOPKG;
+	case RXGK_BADLEVEL:		return -EKEYREJECTED;
+	case RXGK_BADKEYNO:		return -EKEYREJECTED;
+	case RXGK_EXPIRED:		return -EKEYEXPIRED;
+	case RXGK_NOTAUTH:		return -EKEYREJECTED;
+	case RXGK_BAD_TOKEN:		return -EKEYREJECTED;
+	case RXGK_SEALED_INCON:		return -EKEYREJECTED;
+	case RXGK_DATA_LEN:		return -EPROTO;
+	case RXGK_BAD_QOP:		return -EKEYREJECTED;
+
 	case RXGEN_OPCODE:	return -ENOTSUPP;
 
 	default:		return -EREMOTEIO;
diff --git a/include/uapi/linux/rxrpc.h b/include/uapi/linux/rxrpc.h
index 8f8dc7a937a4..0e296d219191 100644
--- a/include/uapi/linux/rxrpc.h
+++ b/include/uapi/linux/rxrpc.h
@@ -73,6 +73,7 @@ enum rxrpc_cmsg_type {
 #define RXRPC_SECURITY_RXKAD	2	/* kaserver or kerberos 4 */
 #define RXRPC_SECURITY_RXGK	4	/* gssapi-based */
 #define RXRPC_SECURITY_RXK5	5	/* kerberos 5 */
+#define RXRPC_SECURITY_YFS_RXGK	6	/* YFS gssapi-based */
 
 /*
  * RxRPC-level abort codes
@@ -118,4 +119,20 @@ enum rxrpc_cmsg_type {
 #define RXKADDATALEN		19270411	/* user data too long */
 #define RXKADILLEGALLEVEL	19270412	/* caller not authorised to use encrypted conns */
 
+/*
+ * RxGK GSSAPI security abort codes.
+ */
+#define RXGK_INCONSISTENCY	1233242880	/* Security module structure inconsistent */
+#define RXGK_PACKETSHORT	1233242881	/* Packet too short for security challenge */
+#define RXGK_BADCHALLENGE	1233242882	/* Invalid security challenge */
+#define RXGK_BADETYPE		1233242883	/* Invalid or impermissible encryption type */
+#define RXGK_BADLEVEL		1233242884	/* Invalid or impermissible security level */
+#define RXGK_BADKEYNO		1233242885	/* Key version number not found */
+#define RXGK_EXPIRED		1233242886	/* Token has expired */
+#define RXGK_NOTAUTH		1233242887	/* Caller not authorized */
+#define RXGK_BAD_TOKEN		1233242888	/* Security object was passed a bad token */
+#define RXGK_SEALED_INCON	1233242889	/* Sealed data inconsistent */
+#define RXGK_DATA_LEN		1233242890	/* User data too long */
+#define RXGK_BAD_QOP		1233242891	/* Inadequate quality of protection available */
+
 #endif /* _UAPI_LINUX_RXRPC_H */



From dhowells at redhat.com  Fri Jan 17 10:35:30 2025
From: dhowells at redhat.com (David Howells)
Date: Fri, 17 Jan 2025 18:35:30 +0000
Subject: [RFC PATCH 21/24] rxrpc: Add YFS RxGK (GSSAPI) security class
In-Reply-To: <20250117183538.881618-1-dhowells@redhat.com>
References: <20250117183538.881618-1-dhowells@redhat.com>
Message-ID: <20250117183538.881618-22-dhowells@redhat.com>

Add support for the YFS-variant RxGK security class to support
GSSAPI-derived authentication.  This also allows the use of better crypto
over the rxkad security class.

The key payload is XDR encoded of the form:

    typedef int64_t opr_time;

    const AFSTOKEN_RK_TIX_MAX = 12000; 	/* Matches entry in rxkad.h */

    struct token_rxkad {
	afs_int32 viceid;
	afs_int32 kvno;
	afs_int64 key;
	afs_int32 begintime;
	afs_int32 endtime;
	afs_int32 primary_flag;
	opaque ticket<AFSTOKEN_RK_TIX_MAX>;
    };

    struct token_rxgk {
	opr_time begintime;
	opr_time endtime;
	afs_int64 level;
	afs_int64 lifetime;
	afs_int64 bytelife;
	afs_int64 enctype;
	opaque key<>;
	opaque ticket<>;
    };

    const AFSTOKEN_UNION_NOAUTH = 0;
    const AFSTOKEN_UNION_KAD = 2;
    const AFSTOKEN_UNION_YFSGK = 6;

    union ktc_tokenUnion switch (afs_int32 type) {
	case AFSTOKEN_UNION_KAD:
	    token_rxkad kad;
	case AFSTOKEN_UNION_YFSGK:
	    token_rxgk  gk;
    };

    const AFSTOKEN_LENGTH_MAX = 16384;
    typedef opaque token_opaque<AFSTOKEN_LENGTH_MAX>;

    const AFSTOKEN_MAX = 8;
    const AFSTOKEN_CELL_MAX = 64;

    struct ktc_setTokenData {
	afs_int32 flags;
	string cell<AFSTOKEN_CELL_MAX>;
	token_opaque tokens<AFSTOKEN_MAX>;
    };

The parser for the basic token struct is already present, as is the rxkad
token type.  This adds a parser for the rxgk token type.

Signed-off-by: David Howells <dhowells at redhat.com>
cc: Marc Dionne <marc.dionne at auristor.com>
cc: Herbert Xu <herbert at gondor.apana.org.au>
cc: "David S. Miller" <davem at davemloft.net>
cc: Chuck Lever <chuck.lever at oracle.com>
cc: Eric Dumazet <edumazet at google.com>
cc: Jakub Kicinski <kuba at kernel.org>
cc: Paolo Abeni <pabeni at redhat.com>
cc: Simon Horman <horms at kernel.org>
cc: linux-afs at lists.infradead.org
cc: linux-crypto at vger.kernel.org
cc: netdev at vger.kernel.org
---
 include/keys/rxrpc-type.h |  17 ++++
 net/rxrpc/key.c           | 183 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 200 insertions(+)

diff --git a/include/keys/rxrpc-type.h b/include/keys/rxrpc-type.h
index 333c0f49a9cd..0ddbe197a261 100644
--- a/include/keys/rxrpc-type.h
+++ b/include/keys/rxrpc-type.h
@@ -9,6 +9,7 @@
 #define _KEYS_RXRPC_TYPE_H
 
 #include <linux/key.h>
+#include <crypto/krb5.h>
 
 /*
  * key type for AF_RXRPC keys
@@ -31,6 +32,21 @@ struct rxkad_key {
 	u8	ticket[];		/* the encrypted ticket */
 };
 
+/*
+ * RxRPC key for YFS-RxGK (type-6 security)
+ */
+struct rxgk_key {
+	s64		begintime;	/* Time at which the ticket starts */
+	s64		endtime;	/* Time at which the ticket ends */
+	u64		lifetime;	/* Maximum lifespan of a connection (seconds) */
+	u64		bytelife;	/* Maximum number of bytes on a connection */
+	unsigned int	enctype;	/* Encoding type */
+	s8		level;		/* Negotiated security RXRPC_SECURITY_PLAIN/AUTH/ENCRYPT */
+	struct krb5_buffer key;		/* Master key, K0 */
+	struct krb5_buffer ticket;	/* Ticket to be passed to server */
+	u8		_key[];		/* Key storage */
+};
+
 /*
  * list of tokens attached to an rxrpc key
  */
@@ -40,6 +56,7 @@ struct rxrpc_key_token {
 	struct rxrpc_key_token *next;	/* the next token in the list */
 	union {
 		struct rxkad_key *kad;
+		struct rxgk_key *rxgk;
 	};
 };
 
diff --git a/net/rxrpc/key.c b/net/rxrpc/key.c
index 33e8302a79e3..2e42a0bbeac2 100644
--- a/net/rxrpc/key.c
+++ b/net/rxrpc/key.c
@@ -129,6 +129,158 @@ static int rxrpc_preparse_xdr_rxkad(struct key_preparsed_payload *prep,
 	return 0;
 }
 
+static u64 xdr_dec64(const __be32 *xdr)
+{
+	return (u64)ntohl(xdr[0]) << 32 | (u64)ntohl(xdr[1]);
+}
+
+static time64_t rxrpc_s64_to_time64(s64 time_in_100ns)
+{
+	bool neg = false;
+	u64 tmp = time_in_100ns;
+
+	if (time_in_100ns < 0) {
+		tmp = -time_in_100ns;
+		neg = true;
+	}
+	do_div(tmp, 10000000);
+	return neg ? -tmp : tmp;
+}
+
+/*
+ * Parse a YFS-RxGK type XDR format token
+ * - the caller guarantees we have at least 4 words
+ *
+ * struct token_rxgk {
+ *	opr_time begintime;
+ *	opr_time endtime;
+ *	afs_int64 level;
+ *	afs_int64 lifetime;
+ *	afs_int64 bytelife;
+ *	afs_int64 enctype;
+ *	opaque key<>;
+ *	opaque ticket<>;
+ * };
+ */
+static int rxrpc_preparse_xdr_yfs_rxgk(struct key_preparsed_payload *prep,
+				       size_t datalen,
+				       const __be32 *xdr, unsigned int toklen)
+{
+	struct rxrpc_key_token *token, **pptoken;
+	time64_t expiry;
+	size_t plen;
+	const __be32 *ticket, *key;
+	s64 tmp;
+	u32 tktlen, keylen;
+
+	_enter(",{%x,%x,%x,%x},%x",
+	       ntohl(xdr[0]), ntohl(xdr[1]), ntohl(xdr[2]), ntohl(xdr[3]),
+	       toklen);
+
+	if (6 * 2 + 2 > toklen / 4)
+		goto reject;
+
+	key = xdr + (6 * 2 + 1);
+	keylen = ntohl(key[-1]);
+	_debug("keylen: %x", keylen);
+	keylen = round_up(keylen, 4);
+	if ((6 * 2 + 2) * 4 + keylen > toklen)
+		goto reject;
+
+	ticket = xdr + (6 * 2 + 1 + (keylen / 4) + 1);
+	tktlen = ntohl(ticket[-1]);
+	_debug("tktlen: %x", tktlen);
+	tktlen = round_up(tktlen, 4);
+	if ((6 * 2 + 2) * 4 + keylen + tktlen != toklen) {
+		kleave(" = -EKEYREJECTED [%x!=%x, %x,%x]",
+		       (6 * 2 + 2) * 4 + keylen + tktlen, toklen,
+		       keylen, tktlen);
+		goto reject;
+	}
+
+	plen = sizeof(*token) + sizeof(*token->rxgk) + tktlen + keylen;
+	prep->quotalen = datalen + plen;
+
+	plen -= sizeof(*token);
+	token = kzalloc(sizeof(*token), GFP_KERNEL);
+	if (!token)
+		goto nomem;
+
+	token->rxgk = kzalloc(sizeof(*token->rxgk) + keylen, GFP_KERNEL);
+	if (!token->rxgk)
+		goto nomem_token;
+
+	token->security_index	= RXRPC_SECURITY_YFS_RXGK;
+	token->rxgk->begintime	= xdr_dec64(xdr + 0 * 2);
+	token->rxgk->endtime	= xdr_dec64(xdr + 1 * 2);
+	token->rxgk->level	= tmp = xdr_dec64(xdr + 2 * 2);
+	if (tmp < -1LL || tmp > RXRPC_SECURITY_ENCRYPT)
+		goto reject_token;
+	token->rxgk->lifetime	= xdr_dec64(xdr + 3 * 2);
+	token->rxgk->bytelife	= xdr_dec64(xdr + 4 * 2);
+	token->rxgk->enctype	= tmp = xdr_dec64(xdr + 5 * 2);
+	if (tmp < 0 || tmp > UINT_MAX)
+		goto reject_token;
+	token->rxgk->key.len	= ntohl(key[-1]);
+	token->rxgk->key.data	= token->rxgk->_key;
+	token->rxgk->ticket.len = ntohl(ticket[-1]);
+
+	expiry = rxrpc_s64_to_time64(token->rxgk->endtime);
+	if (expiry < 0)
+		goto expired;
+	if (expiry < prep->expiry)
+		prep->expiry = expiry;
+
+	memcpy(token->rxgk->key.data, key, token->rxgk->key.len);
+
+	/* Pad the ticket so that we can use it directly in XDR */
+	token->rxgk->ticket.data = kzalloc(round_up(token->rxgk->ticket.len, 4),
+					   GFP_KERNEL);
+	if (!token->rxgk->ticket.data)
+		goto nomem_yrxgk;
+	memcpy(token->rxgk->ticket.data, ticket, token->rxgk->ticket.len);
+
+	_debug("SCIX: %u",	token->security_index);
+	_debug("EXPY: %llx",	token->rxgk->endtime);
+	_debug("LIFE: %llx",	token->rxgk->lifetime);
+	_debug("BYTE: %llx",	token->rxgk->bytelife);
+	_debug("ENC : %u",	token->rxgk->enctype);
+	_debug("LEVL: %u",	token->rxgk->level);
+	_debug("KLEN: %u",	token->rxgk->key.len);
+	_debug("TLEN: %u",	token->rxgk->ticket.len);
+	_debug("KEY0: %*phN",	token->rxgk->key.len, token->rxgk->key.data);
+	_debug("TICK: %*phN",
+	       min_t(u32, token->rxgk->ticket.len, 32), token->rxgk->ticket.data);
+
+	/* count the number of tokens attached */
+	prep->payload.data[1] = (void *)((unsigned long)prep->payload.data[1] + 1);
+
+	/* attach the data */
+	for (pptoken = (struct rxrpc_key_token **)&prep->payload.data[0];
+	     *pptoken;
+	     pptoken = &(*pptoken)->next)
+		continue;
+	*pptoken = token;
+
+	_leave(" = 0");
+	return 0;
+
+nomem_yrxgk:
+	kfree(token->rxgk);
+nomem_token:
+	kfree(token);
+nomem:
+	return -ENOMEM;
+reject_token:
+	kfree(token);
+reject:
+	return -EKEYREJECTED;
+expired:
+	kfree(token->rxgk);
+	kfree(token);
+	return -EKEYEXPIRED;
+}
+
 /*
  * attempt to parse the data as the XDR format
  * - the caller guarantees we have more than 7 words
@@ -228,6 +380,9 @@ static int rxrpc_preparse_xdr(struct key_preparsed_payload *prep)
 		case RXRPC_SECURITY_RXKAD:
 			ret2 = rxrpc_preparse_xdr_rxkad(prep, datalen, token, toklen);
 			break;
+		case RXRPC_SECURITY_YFS_RXGK:
+			ret2 = rxrpc_preparse_xdr_yfs_rxgk(prep, datalen, token, toklen);
+			break;
 		default:
 			ret2 = -EPROTONOSUPPORT;
 			break;
@@ -390,6 +545,10 @@ static void rxrpc_free_token_list(struct rxrpc_key_token *token)
 		case RXRPC_SECURITY_RXKAD:
 			kfree(token->kad);
 			break;
+		case RXRPC_SECURITY_YFS_RXGK:
+			kfree(token->rxgk->ticket.data);
+			kfree(token->rxgk);
+			break;
 		default:
 			pr_err("Unknown token type %x on rxrpc key\n",
 			       token->security_index);
@@ -433,6 +592,9 @@ static void rxrpc_describe(const struct key *key, struct seq_file *m)
 		case RXRPC_SECURITY_RXKAD:
 			seq_puts(m, "ka");
 			break;
+		case RXRPC_SECURITY_YFS_RXGK:
+			seq_puts(m, "ygk");
+			break;
 		default: /* we have a ticket we can't encode */
 			seq_printf(m, "%u", token->security_index);
 			break;
@@ -595,6 +757,13 @@ static long rxrpc_read(const struct key *key,
 				toksize += RND(token->kad->ticket_len);
 			break;
 
+		case RXRPC_SECURITY_YFS_RXGK:
+			toksize += 6 * 8 + 2 * 4;
+			if (!token->no_leak_key)
+				toksize += RND(token->rxgk->key.len);
+			toksize += RND(token->rxgk->ticket.len);
+			break;
+
 		default: /* we have a ticket we can't encode */
 			pr_err("Unsupported key token type (%u)\n",
 			       token->security_index);
@@ -674,6 +843,20 @@ static long rxrpc_read(const struct key *key,
 				ENCODE_DATA(token->kad->ticket_len, token->kad->ticket);
 			break;
 
+		case RXRPC_SECURITY_YFS_RXGK:
+			ENCODE64(token->rxgk->begintime);
+			ENCODE64(token->rxgk->endtime);
+			ENCODE64(token->rxgk->level);
+			ENCODE64(token->rxgk->lifetime);
+			ENCODE64(token->rxgk->bytelife);
+			ENCODE64(token->rxgk->enctype);
+			if (token->no_leak_key)
+				ENCODE(0);
+			else
+				ENCODE_DATA(token->rxgk->key.len, token->rxgk->key.data);
+			ENCODE_DATA(token->rxgk->ticket.len, token->rxgk->ticket.data);
+			break;
+
 		default:
 			pr_err("Unsupported key token type (%u)\n",
 			       token->security_index);



From dhowells at redhat.com  Fri Jan 17 10:35:31 2025
From: dhowells at redhat.com (David Howells)
Date: Fri, 17 Jan 2025 18:35:31 +0000
Subject: [RFC PATCH 22/24] rxrpc: rxgk: Provide infrastructure and key derivation
In-Reply-To: <20250117183538.881618-1-dhowells@redhat.com>
References: <20250117183538.881618-1-dhowells@redhat.com>
Message-ID: <20250117183538.881618-23-dhowells@redhat.com>

Provide some infrastructure for implementing the RxGK transport security
class:

 (1) A definition of an encoding type, including:

	- Relevant crypto-layer names
	- Lengths of the crypto keys and checksums involved
	- Crypto functions specific to the encoding type
	- Crypto scheme used for that type

 (2) A definition of a crypto scheme, including:

	- Underlying crypto handlers
	- The pseudo-random function, PRF, used in base key derivation
	- Functions for deriving usage keys Kc, Ke and Ki
	- Functions for en/decrypting parts of an sk_buff

 (3) A key context, with the usage keys required for a derivative of a
     transport key for a specific key number.  This includes keys for
     securing packets for transmission, extracting received packets and
     dealing with response packets.

 (3) A function to look up an encoding type by number.

 (4) A function to set up a key context and derive the keys.

 (5) A function to set up the keys required to extract the ticket obtained
     from the GSS negotiation in the server.

 (6) Miscellaneous functions for context handling.

The keys and key derivation functions are described in:

	tools.ietf.org/html/draft-wilkinson-afs3-rxgk-11

Signed-off-by: David Howells <dhowells at redhat.com>
cc: Marc Dionne <marc.dionne at auristor.com>
cc: Herbert Xu <herbert at gondor.apana.org.au>
cc: "David S. Miller" <davem at davemloft.net>
cc: Chuck Lever <chuck.lever at oracle.com>
cc: Eric Dumazet <edumazet at google.com>
cc: Jakub Kicinski <kuba at kernel.org>
cc: Paolo Abeni <pabeni at redhat.com>
cc: Simon Horman <horms at kernel.org>
cc: linux-afs at lists.infradead.org
cc: linux-crypto at vger.kernel.org
cc: netdev at vger.kernel.org
---
 net/rxrpc/Kconfig       |  10 ++
 net/rxrpc/Makefile      |   3 +-
 net/rxrpc/ar-internal.h |   3 +
 net/rxrpc/rxgk_common.h |  48 +++++++
 net/rxrpc/rxgk_kdf.c    | 287 ++++++++++++++++++++++++++++++++++++++++
 5 files changed, 350 insertions(+), 1 deletion(-)
 create mode 100644 net/rxrpc/rxgk_common.h
 create mode 100644 net/rxrpc/rxgk_kdf.c

diff --git a/net/rxrpc/Kconfig b/net/rxrpc/Kconfig
index a20986806fea..0a2b38b9b94a 100644
--- a/net/rxrpc/Kconfig
+++ b/net/rxrpc/Kconfig
@@ -67,6 +67,16 @@ config RXKAD
 
 	  See Documentation/networking/rxrpc.rst.
 
+config RXGK
+	bool "RxRPC GSSAPI security"
+	depends on AF_RXRPC
+	depends on CRYPTO_KRB5
+	help
+	  Provide the GSSAPI-based RxGK security class for AFS.  Keys are added
+	  with add_key().
+
+	  See Documentation/networking/rxrpc.rst.
+
 config RXPERF
 	tristate "RxRPC test service"
 	help
diff --git a/net/rxrpc/Makefile b/net/rxrpc/Makefile
index 210b75e3179e..9c8eb1471054 100644
--- a/net/rxrpc/Makefile
+++ b/net/rxrpc/Makefile
@@ -39,6 +39,7 @@ rxrpc-y := \
 rxrpc-$(CONFIG_PROC_FS) += proc.o
 rxrpc-$(CONFIG_RXKAD) += rxkad.o
 rxrpc-$(CONFIG_SYSCTL) += sysctl.o
-
+rxrpc-$(CONFIG_RXGK) += \
+	rxgk_kdf.o
 
 obj-$(CONFIG_RXPERF) += rxperf.o
diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h
index 718193df9d2e..2392f2e062c2 100644
--- a/net/rxrpc/ar-internal.h
+++ b/net/rxrpc/ar-internal.h
@@ -526,6 +526,9 @@ struct rxrpc_connection {
 			struct rxrpc_crypt csum_iv;	/* packet checksum base */
 			u32	nonce;		/* response re-use preventer */
 		} rxkad;
+		struct {
+			u64	start_time;	/* The start time for TK derivation */
+		} rxgk;
 	};
 	unsigned long		flags;
 	unsigned long		events;
diff --git a/net/rxrpc/rxgk_common.h b/net/rxrpc/rxgk_common.h
new file mode 100644
index 000000000000..c07a5386b303
--- /dev/null
+++ b/net/rxrpc/rxgk_common.h
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* Common bits for GSSAPI-based RxRPC security.
+ *
+ * Copyright (C) 2023 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells at redhat.com)
+ */
+
+#include <crypto/krb5.h>
+#include <crypto/skcipher.h>
+#include <crypto/hash.h>
+
+/*
+ * Per-key number context.  This is replaced when the connection is rekeyed.
+ */
+struct rxgk_context {
+	refcount_t		usage;
+	unsigned int		key_number;	/* Rekeying number (goes in the rx header) */
+	unsigned long		flags;
+#define RXGK_TK_NEEDS_REKEY	0		/* Set if this needs rekeying */
+	unsigned long		expiry;		/* Expiration time of this key */
+	long long		bytes_remaining; /* Remaining Tx lifetime of this key */
+	const struct krb5_enctype *krb5;	/* RxGK encryption type */
+	const struct rxgk_key	*key;
+
+	/* We need up to 7 keys derived from the transport key, but we don't
+	 * actually need the transport key.  Each key is derived by
+	 * DK(TK,constant).
+	 */
+	struct crypto_aead	*tx_enc;	/* Transmission key */
+	struct crypto_aead	*rx_enc;	/* Reception key */
+	struct crypto_shash	*tx_Kc;		/* Transmission checksum key */
+	struct crypto_shash	*rx_Kc;		/* Reception checksum key */
+	struct crypto_aead	*resp_enc;	/* Response packet enc key */
+};
+
+/*
+ * rxgk_kdf.c
+ */
+void rxgk_put(struct rxgk_context *gk);
+struct rxgk_context *rxgk_generate_transport_key(struct rxrpc_connection *conn,
+						 const struct rxgk_key *key,
+						 unsigned int key_number,
+						 gfp_t gfp);
+int rxgk_set_up_token_cipher(const struct krb5_buffer *server_key,
+			     struct crypto_aead **token_key,
+			     unsigned int enctype,
+			     const struct krb5_enctype **_krb5,
+			     gfp_t gfp);
diff --git a/net/rxrpc/rxgk_kdf.c b/net/rxrpc/rxgk_kdf.c
new file mode 100644
index 000000000000..ef0e57e5a6df
--- /dev/null
+++ b/net/rxrpc/rxgk_kdf.c
@@ -0,0 +1,287 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* RxGK transport key derivation.
+ *
+ * Copyright (C) 2023 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells at redhat.com)
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/key-type.h>
+#include <linux/slab.h>
+#include <keys/rxrpc-type.h>
+#include "ar-internal.h"
+#include "rxgk_common.h"
+
+#define round16(x) (((x) + 15) & ~15)
+
+/*
+ * Constants used to derive the keys and hmacs actually used for doing stuff.
+ */
+#define RXGK_CLIENT_ENC_PACKET		1026U // 0x402
+#define RXGK_CLIENT_MIC_PACKET          1027U // 0x403
+#define RXGK_SERVER_ENC_PACKET          1028U // 0x404
+#define RXGK_SERVER_MIC_PACKET          1029U // 0x405
+#define RXGK_CLIENT_ENC_RESPONSE        1030U // 0x406
+#define RXGK_SERVER_ENC_TOKEN           1036U // 0x40c
+
+static void rxgk_free(struct rxgk_context *gk)
+{
+	if (gk->tx_Kc)
+		crypto_free_shash(gk->tx_Kc);
+	if (gk->rx_Kc)
+		crypto_free_shash(gk->rx_Kc);
+	if (gk->tx_enc)
+		crypto_free_aead(gk->tx_enc);
+	if (gk->rx_enc)
+		crypto_free_aead(gk->rx_enc);
+	if (gk->resp_enc)
+		crypto_free_aead(gk->resp_enc);
+	kfree(gk);
+}
+
+void rxgk_put(struct rxgk_context *gk)
+{
+	if (gk && refcount_dec_and_test(&gk->usage))
+		rxgk_free(gk);
+}
+
+/*
+ * Transport key derivation function.
+ *
+ *      TK = random-to-key(PRF+(K0, L,
+ *                         epoch || cid || start_time || key_number))
+ *      [tools.ietf.org/html/draft-wilkinson-afs3-rxgk-11 sec 8.3]
+ */
+static int rxgk_derive_transport_key(struct rxrpc_connection *conn,
+				     struct rxgk_context *gk,
+				     const struct rxgk_key *rxgk,
+				     struct krb5_buffer *TK,
+				     gfp_t gfp)
+{
+	const struct krb5_enctype *krb5 = gk->krb5;
+	struct krb5_buffer conn_info;
+	unsigned int L = krb5->key_bytes;
+	__be32 *info;
+	u8 *buffer;
+	int ret;
+
+	_enter("");
+
+	conn_info.len = sizeof(__be32) * 5;
+
+	buffer = kzalloc(round16(conn_info.len), gfp);
+	if (!buffer)
+		return -ENOMEM;
+
+	conn_info.data = buffer;
+
+	info = (__be32 *)conn_info.data;
+	info[0] = htonl(conn->proto.epoch);
+	info[1] = htonl(conn->proto.cid);
+	info[2] = htonl(conn->rxgk.start_time >> 32);
+	info[3] = htonl(conn->rxgk.start_time >>  0);
+	info[4] = htonl(gk->key_number);
+
+	ret = crypto_krb5_calc_PRFplus(krb5, &rxgk->key, L, &conn_info, TK, gfp);
+	kfree_sensitive(buffer);
+	_leave(" = %d", ret);
+	return ret;
+}
+
+/*
+ * Set up the ciphers for the usage keys.
+ */
+static int rxgk_set_up_ciphers(struct rxrpc_connection *conn,
+			       struct rxgk_context *gk,
+			       const struct rxgk_key *rxgk,
+			       gfp_t gfp)
+{
+	const struct krb5_enctype *krb5 = gk->krb5;
+	struct crypto_shash *shash;
+	struct crypto_aead *aead;
+	struct krb5_buffer TK;
+	bool service = rxrpc_conn_is_service(conn);
+	int ret;
+	u8 *buffer;
+
+	buffer = kzalloc(krb5->key_bytes, gfp);
+	if (!buffer)
+		return -ENOMEM;
+
+	TK.len = krb5->key_bytes;
+	TK.data = buffer;
+
+	ret = rxgk_derive_transport_key(conn, gk, rxgk, &TK, gfp);
+	if (ret < 0)
+		goto out;
+
+	aead = crypto_krb5_prepare_encryption(krb5, &TK, RXGK_CLIENT_ENC_RESPONSE, gfp);
+	if (IS_ERR(aead))
+		goto aead_error;
+	gk->resp_enc = aead;
+
+	if (crypto_aead_blocksize(gk->resp_enc) != krb5->block_len ||
+	    crypto_aead_authsize(gk->resp_enc) != krb5->cksum_len) {
+		pr_notice("algo inconsistent with krb5 table %u!=%u or %u!=%u\n",
+			  crypto_aead_blocksize(gk->resp_enc), krb5->block_len,
+			  crypto_aead_authsize(gk->resp_enc), krb5->cksum_len);
+		return -EINVAL;
+	}
+
+	if (service) {
+		switch (conn->security_level) {
+		case RXRPC_SECURITY_AUTH:
+			shash = crypto_krb5_prepare_checksum(
+				krb5, &TK, RXGK_SERVER_MIC_PACKET, gfp);
+			if (IS_ERR(shash))
+				goto hash_error;
+			gk->tx_Kc = shash;
+			shash = crypto_krb5_prepare_checksum(
+				krb5, &TK, RXGK_CLIENT_MIC_PACKET, gfp);
+			if (IS_ERR(shash))
+				goto hash_error;
+			gk->rx_Kc = shash;
+			break;
+		case RXRPC_SECURITY_ENCRYPT:
+			aead = crypto_krb5_prepare_encryption(
+				krb5, &TK, RXGK_SERVER_ENC_PACKET, gfp);
+			if (IS_ERR(aead))
+				goto aead_error;
+			gk->tx_enc = aead;
+			aead = crypto_krb5_prepare_encryption(
+				krb5, &TK, RXGK_CLIENT_ENC_PACKET, gfp);
+			if (IS_ERR(aead))
+				goto aead_error;
+			gk->rx_enc = aead;
+			break;
+		}
+	} else {
+		switch (conn->security_level) {
+		case RXRPC_SECURITY_AUTH:
+			shash = crypto_krb5_prepare_checksum(
+				krb5, &TK, RXGK_CLIENT_MIC_PACKET, gfp);
+			if (IS_ERR(shash))
+				goto hash_error;
+			gk->tx_Kc = shash;
+			shash = crypto_krb5_prepare_checksum(
+				krb5, &TK, RXGK_SERVER_MIC_PACKET, gfp);
+			if (IS_ERR(shash))
+				goto hash_error;
+			gk->rx_Kc = shash;
+			break;
+		case RXRPC_SECURITY_ENCRYPT:
+			aead = crypto_krb5_prepare_encryption(
+				krb5, &TK, RXGK_CLIENT_ENC_PACKET, gfp);
+			if (IS_ERR(aead))
+				goto aead_error;
+			gk->tx_enc = aead;
+			aead = crypto_krb5_prepare_encryption(
+				krb5, &TK, RXGK_SERVER_ENC_PACKET, gfp);
+			if (IS_ERR(aead))
+				goto aead_error;
+			gk->rx_enc = aead;
+			break;
+		}
+	}
+
+	ret = 0;
+out:
+	kfree_sensitive(buffer);
+	return ret;
+aead_error:
+	ret = PTR_ERR(aead);
+	goto out;
+hash_error:
+	ret = PTR_ERR(shash);
+	goto out;
+}
+
+/*
+ * Derive a transport key for a connection and then derive a bunch of usage
+ * keys from it and set up ciphers using them.
+ */
+struct rxgk_context *rxgk_generate_transport_key(struct rxrpc_connection *conn,
+						 const struct rxgk_key *key,
+						 unsigned int key_number,
+						 gfp_t gfp)
+{
+	struct rxgk_context *gk;
+	unsigned long lifetime;
+	int ret = -ENOPKG;
+
+	_enter("");
+
+	gk = kzalloc(sizeof(*gk), GFP_KERNEL);
+	if (!gk)
+		return ERR_PTR(-ENOMEM);
+	refcount_set(&gk->usage, 1);
+	gk->key		= key;
+	gk->key_number	= key_number;
+
+	gk->krb5 = crypto_krb5_find_enctype(key->enctype);
+	if (!gk->krb5)
+		goto err_tk;
+
+	ret = rxgk_set_up_ciphers(conn, gk, key, gfp);
+	if (ret)
+		goto err_tk;
+
+	/* Set the remaining number of bytes encrypted with this key that may
+	 * be transmitted before rekeying.  Note that the spec has been
+	 * interpreted differently on this point...
+	 */
+	switch (key->bytelife) {
+	case 0:
+	case 63:
+		gk->bytes_remaining = LLONG_MAX;
+		break;
+	case 1 ... 62:
+		gk->bytes_remaining = 1LL << key->bytelife;
+		break;
+	default:
+		gk->bytes_remaining = key->bytelife;
+		break;
+	}
+
+	/* Set the time after which rekeying must occur */
+	if (key->lifetime) {
+		lifetime = min_t(u64, key->lifetime, INT_MAX / HZ);
+		lifetime *= HZ;
+	} else {
+		lifetime = MAX_JIFFY_OFFSET;
+	}
+	gk->expiry = jiffies + lifetime;
+	return gk;
+
+err_tk:
+	rxgk_put(gk);
+	_leave(" = %d", ret);
+	return ERR_PTR(ret);
+}
+
+/*
+ * Use the server secret key to set up the ciphers that will be used to extract
+ * the token from a response packet.
+ */
+int rxgk_set_up_token_cipher(const struct krb5_buffer *server_key,
+			     struct crypto_aead **token_aead,
+			     unsigned int enctype,
+			     const struct krb5_enctype **_krb5,
+			     gfp_t gfp)
+{
+	const struct krb5_enctype *krb5;
+	struct crypto_aead *aead;
+
+	krb5 = crypto_krb5_find_enctype(enctype);
+	if (!krb5)
+		return -ENOPKG;
+
+	aead = crypto_krb5_prepare_encryption(krb5, server_key, RXGK_SERVER_ENC_TOKEN, gfp);
+	if (IS_ERR(aead))
+		return PTR_ERR(aead);
+
+	*_krb5 = krb5;
+	*token_aead = aead;
+	return 0;
+}



From dhowells at redhat.com  Fri Jan 17 10:35:32 2025
From: dhowells at redhat.com (David Howells)
Date: Fri, 17 Jan 2025 18:35:32 +0000
Subject: [RFC PATCH 23/24] rxrpc: rxgk: Implement the yfs-rxgk security class (GSSAPI)
In-Reply-To: <20250117183538.881618-1-dhowells@redhat.com>
References: <20250117183538.881618-1-dhowells@redhat.com>
Message-ID: <20250117183538.881618-24-dhowells@redhat.com>

Implement the basic parts of the yfs-rxgk security class (security index 6)
to support GSSAPI-negotiated security.

Signed-off-by: David Howells <dhowells at redhat.com>
cc: Marc Dionne <marc.dionne at auristor.com>
cc: Herbert Xu <herbert at gondor.apana.org.au>
cc: "David S. Miller" <davem at davemloft.net>
cc: Chuck Lever <chuck.lever at oracle.com>
cc: Eric Dumazet <edumazet at google.com>
cc: Jakub Kicinski <kuba at kernel.org>
cc: Paolo Abeni <pabeni at redhat.com>
cc: Simon Horman <horms at kernel.org>
cc: linux-afs at lists.infradead.org
cc: linux-crypto at vger.kernel.org
cc: netdev at vger.kernel.org
---
 include/trace/events/rxrpc.h |   36 ++
 net/rxrpc/Makefile           |    2 +
 net/rxrpc/ar-internal.h      |   16 +
 net/rxrpc/conn_event.c       |    2 +-
 net/rxrpc/output.c           |    2 +-
 net/rxrpc/protocol.h         |   20 +
 net/rxrpc/rxgk.c             | 1026 ++++++++++++++++++++++++++++++++++
 net/rxrpc/rxgk_app.c         |  284 ++++++++++
 net/rxrpc/rxgk_common.h      |   92 +++
 net/rxrpc/rxkad.c            |    6 +-
 net/rxrpc/security.c         |    3 +
 11 files changed, 1485 insertions(+), 4 deletions(-)
 create mode 100644 net/rxrpc/rxgk.c
 create mode 100644 net/rxrpc/rxgk_app.c

diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h
index 2f119d18a061..168f3ab7c0e4 100644
--- a/include/trace/events/rxrpc.h
+++ b/include/trace/events/rxrpc.h
@@ -68,6 +68,38 @@
 	EM(rxkad_abort_resp_tkt_sname,		"rxkad-resp-tk-sname")	\
 	EM(rxkad_abort_resp_unknown_tkt,	"rxkad-resp-unknown-tkt") \
 	EM(rxkad_abort_resp_version,		"rxkad-resp-version")	\
+	/* RxGK security errors */					\
+	EM(rxgk_abort_1_verify_mic_eproto,	"rxgk1-vfy-mic-eproto")	\
+	EM(rxgk_abort_2_decrypt_eproto,		"rxgk2-dec-eproto")	\
+	EM(rxgk_abort_2_short_data,		"rxgk2-short-data")	\
+	EM(rxgk_abort_2_short_encdata,		"rxgk2-short-encdata")	\
+	EM(rxgk_abort_2_short_header,		"rxgk2-short-hdr")	\
+	EM(rxgk_abort_bad_key_number,		"rxgk-bad-key-num")	\
+	EM(rxgk_abort_chall_key_expired,	"rxgk-chall-key-exp")	\
+	EM(rxgk_abort_chall_no_key,		"rxgk-chall-nokey")	\
+	EM(rxgk_abort_chall_short,		"rxgk-chall-short")	\
+	EM(rxgk_abort_resp_auth_dec,		"rxgk-resp-auth-dec")	\
+	EM(rxgk_abort_resp_bad_callid,		"rxgk-resp-bad-callid")	\
+	EM(rxgk_abort_resp_bad_nonce,		"rxgk-resp-bad-nonce")	\
+	EM(rxgk_abort_resp_bad_param,		"rxgk-resp-bad-param")	\
+	EM(rxgk_abort_resp_call_ctr,		"rxgk-resp-call-ctr")	\
+	EM(rxgk_abort_resp_call_state,		"rxgk-resp-call-state")	\
+	EM(rxgk_abort_resp_internal_error,	"rxgk-resp-int-error")	\
+	EM(rxgk_abort_resp_nopkg,		"rxgk-resp-nopkg")	\
+	EM(rxgk_abort_resp_short_applen,	"rxgk-resp-short-applen") \
+	EM(rxgk_abort_resp_short_auth,		"rxgk-resp-short-auth") \
+	EM(rxgk_abort_resp_short_call_list,	"rxgk-resp-short-callls") \
+	EM(rxgk_abort_resp_short_packet,	"rxgk-resp-short-packet") \
+	EM(rxgk_abort_resp_short_yfs_klen,	"rxgk-resp-short-yfs-klen") \
+	EM(rxgk_abort_resp_short_yfs_key,	"rxgk-resp-short-yfs-key") \
+	EM(rxgk_abort_resp_short_yfs_tkt,	"rxgk-resp-short-yfs-tkt") \
+	EM(rxgk_abort_resp_tok_dec,		"rxgk-resp-tok-dec")	\
+	EM(rxgk_abort_resp_tok_internal_error,	"rxgk-resp-tok-int-err") \
+	EM(rxgk_abort_resp_tok_keyerr,		"rxgk-resp-tok-keyerr")	\
+	EM(rxgk_abort_resp_tok_nokey,		"rxgk-resp-tok-nokey")	\
+	EM(rxgk_abort_resp_tok_nopkg,		"rxgk-resp-tok-nopkg")	\
+	EM(rxgk_abort_resp_tok_short,		"rxgk-resp-tok-short")	\
+	EM(rxgk_abort_resp_xdr_align,		"rxgk-resp-xdr-align")	\
 	/* rxrpc errors */						\
 	EM(rxrpc_abort_call_improper_term,	"call-improper-term")	\
 	EM(rxrpc_abort_call_reset,		"call-reset")		\
@@ -454,6 +486,8 @@
 	EM(rxrpc_tx_point_call_final_resend,	"CallFinalResend") \
 	EM(rxrpc_tx_point_conn_abort,		"ConnAbort") \
 	EM(rxrpc_tx_point_reject,		"Reject") \
+	EM(rxrpc_tx_point_rxgk_challenge,	"RxGKChall") \
+	EM(rxrpc_tx_point_rxgk_response,	"RxGKResp") \
 	EM(rxrpc_tx_point_rxkad_challenge,	"RxkadChall") \
 	EM(rxrpc_tx_point_rxkad_response,	"RxkadResp") \
 	EM(rxrpc_tx_point_version_keepalive,	"VerKeepalive") \
@@ -472,6 +506,7 @@
 
 #define rxrpc_txbuf_traces \
 	EM(rxrpc_txbuf_alloc_data,		"ALLOC DATA ")	\
+	EM(rxrpc_txbuf_alloc_response,		"ALLOC RESP ")	\
 	EM(rxrpc_txbuf_free,			"FREE       ")	\
 	EM(rxrpc_txbuf_get_buffer,		"GET BUFFER ")	\
 	EM(rxrpc_txbuf_get_trans,		"GET TRANS  ")	\
@@ -479,6 +514,7 @@
 	EM(rxrpc_txbuf_put_cleaned,		"PUT CLEANED")	\
 	EM(rxrpc_txbuf_put_nomem,		"PUT NOMEM  ")	\
 	EM(rxrpc_txbuf_put_rotated,		"PUT ROTATED")	\
+	EM(rxrpc_txbuf_put_response_tx,		"PUT RESP TX")	\
 	EM(rxrpc_txbuf_put_send_aborted,	"PUT SEND-X ")	\
 	EM(rxrpc_txbuf_put_trans,		"PUT TRANS  ")	\
 	EM(rxrpc_txbuf_see_lost,		"SEE LOST   ")	\
diff --git a/net/rxrpc/Makefile b/net/rxrpc/Makefile
index 9c8eb1471054..2ef05701d6d1 100644
--- a/net/rxrpc/Makefile
+++ b/net/rxrpc/Makefile
@@ -40,6 +40,8 @@ rxrpc-$(CONFIG_PROC_FS) += proc.o
 rxrpc-$(CONFIG_RXKAD) += rxkad.o
 rxrpc-$(CONFIG_SYSCTL) += sysctl.o
 rxrpc-$(CONFIG_RXGK) += \
+	rxgk.o \
+	rxgk_app.o \
 	rxgk_kdf.o
 
 obj-$(CONFIG_RXPERF) += rxperf.o
diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h
index 2392f2e062c2..736dc6ea20ac 100644
--- a/net/rxrpc/ar-internal.h
+++ b/net/rxrpc/ar-internal.h
@@ -31,6 +31,7 @@ struct key_preparsed_payload;
 struct rxrpc_connection;
 struct rxrpc_txbuf;
 struct rxrpc_txqueue;
+struct rxgk_context;
 
 /*
  * Mark applied to socket buffers in skb->mark.  skb->priority is used
@@ -280,6 +281,11 @@ struct rxrpc_security {
 
 	/* clear connection security */
 	void (*clear)(struct rxrpc_connection *);
+
+	/* Default ticket -> key decoder */
+	int (*default_decode_ticket)(struct rxrpc_connection *conn, struct sk_buff *skb,
+				     unsigned int ticket_offset, unsigned int ticket_len,
+				     struct key **_key);
 };
 
 /*
@@ -527,7 +533,9 @@ struct rxrpc_connection {
 			u32	nonce;		/* response re-use preventer */
 		} rxkad;
 		struct {
+			struct rxgk_context *keys[1];
 			u64	start_time;	/* The start time for TK derivation */
+			u8	nonce[20];	/* Response re-use preventer */
 		} rxgk;
 	};
 	unsigned long		flags;
@@ -870,6 +878,8 @@ struct rxrpc_txbuf {
 	unsigned short		len;		/* Amount of data in buffer */
 	unsigned short		space;		/* Remaining data space */
 	unsigned short		offset;		/* Offset of fill point */
+	unsigned short		crypto_header;	/* Size of crypto header */
+	unsigned short		sec_header;	/* Size of security header */
 	unsigned short		pkt_len;	/* Size of packet content */
 	unsigned short		alloc_size;	/* Amount of bufferage allocated */
 	unsigned int		flags;
@@ -1295,6 +1305,7 @@ static inline struct rxrpc_net *rxrpc_net(struct net *net)
 /*
  * output.c
  */
+ssize_t do_udp_sendmsg(struct socket *socket, struct msghdr *msg, size_t len);
 void rxrpc_send_ACK(struct rxrpc_call *call, u8 ack_reason,
 		    rxrpc_serial_t serial, enum rxrpc_propose_ack_trace why);
 void rxrpc_send_probe_for_pmtud(struct rxrpc_call *call);
@@ -1366,6 +1377,11 @@ void rxrpc_call_add_rtt(struct rxrpc_call *call, enum rxrpc_rtt_rx_trace why,
 ktime_t rxrpc_get_rto_backoff(struct rxrpc_call *call, bool retrans);
 void rxrpc_call_init_rtt(struct rxrpc_call *call);
 
+/*
+ * rxgk.c
+ */
+extern const struct rxrpc_security rxgk_yfs;
+
 /*
  * rxkad.c
  */
diff --git a/net/rxrpc/conn_event.c b/net/rxrpc/conn_event.c
index 713e04394ceb..3c1663318626 100644
--- a/net/rxrpc/conn_event.c
+++ b/net/rxrpc/conn_event.c
@@ -57,7 +57,7 @@ int rxrpc_abort_conn(struct rxrpc_connection *conn, struct sk_buff *skb,
 				  sp->hdr.seq, abort_code, err);
 		rxrpc_poke_conn(conn, rxrpc_conn_get_poke_abort);
 	}
-	return -EPROTO;
+	return err;
 }
 
 /*
diff --git a/net/rxrpc/output.c b/net/rxrpc/output.c
index 6f7a125d6e90..6e2def0d8773 100644
--- a/net/rxrpc/output.c
+++ b/net/rxrpc/output.c
@@ -18,7 +18,7 @@
 
 extern int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len);
 
-static ssize_t do_udp_sendmsg(struct socket *socket, struct msghdr *msg, size_t len)
+ssize_t do_udp_sendmsg(struct socket *socket, struct msghdr *msg, size_t len)
 {
 	struct sockaddr *sa = msg->msg_name;
 	struct sock *sk = socket->sk;
diff --git a/net/rxrpc/protocol.h b/net/rxrpc/protocol.h
index 42f70e4636f8..f8bfec12bc7e 100644
--- a/net/rxrpc/protocol.h
+++ b/net/rxrpc/protocol.h
@@ -181,4 +181,24 @@ struct rxkad_response {
 	__be32		ticket_len;	/* Kerberos ticket length  */
 } __packed;
 
+/*
+ * GSSAPI security type-4 and type-6 data header.
+ */
+struct rxgk_header {
+	__be32	epoch;
+	__be32	cid;
+	__be32	call_number;
+	__be32	seq;
+	__be32	sec_index;
+	__be32	data_len;
+} __packed;
+
+/*
+ * GSSAPI security type-4 and type-6 response packet header.
+ */
+struct rxgk_response {
+	__be64	start_time;
+	__be32	token_len;
+} __packed;
+
 #endif /* _LINUX_RXRPC_PACKET_H */
diff --git a/net/rxrpc/rxgk.c b/net/rxrpc/rxgk.c
new file mode 100644
index 000000000000..9c5c616c40cb
--- /dev/null
+++ b/net/rxrpc/rxgk.c
@@ -0,0 +1,1026 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* GSSAPI-based RxRPC security
+ *
+ * Copyright (C) 2023 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells at redhat.com)
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/net.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/key-type.h>
+#include "ar-internal.h"
+#include "rxgk_common.h"
+
+/*
+ * Parse the information from a server key
+ */
+static int rxgk_preparse_server_key(struct key_preparsed_payload *prep)
+{
+	const struct krb5_enctype *krb5;
+	struct krb5_buffer *server_key = (void *)&prep->payload.data[2];
+	unsigned int service, sec_class, kvno, enctype;
+	int n = 0;
+
+	_enter("%zu", prep->datalen);
+
+	if (sscanf(prep->orig_description, "%u:%u:%u:%u%n",
+		   &service, &sec_class, &kvno, &enctype, &n) != 4)
+		return -EINVAL;
+
+	if (prep->orig_description[n])
+		return -EINVAL;
+
+	krb5 = crypto_krb5_find_enctype(enctype);
+	if (!krb5)
+		return -ENOPKG;
+
+	prep->payload.data[0] = (struct krb5_enctype *)krb5;
+
+	if (prep->datalen != krb5->key_len)
+		return -EKEYREJECTED;
+
+	server_key->len = prep->datalen;
+	server_key->data = kmemdup(prep->data, prep->datalen, GFP_KERNEL);
+	if (!server_key->data)
+		return -ENOMEM;
+
+	_leave(" = 0");
+	return 0;
+}
+
+static void rxgk_free_server_key(union key_payload *payload)
+{
+	struct krb5_buffer *server_key = (void *)&payload->data[2];
+
+	kfree_sensitive(server_key->data);
+}
+
+static void rxgk_free_preparse_server_key(struct key_preparsed_payload *prep)
+{
+	rxgk_free_server_key(&prep->payload);
+}
+
+static void rxgk_destroy_server_key(struct key *key)
+{
+	rxgk_free_server_key(&key->payload);
+}
+
+static void rxgk_describe_server_key(const struct key *key, struct seq_file *m)
+{
+	const struct krb5_enctype *krb5 = key->payload.data[0];
+
+	if (krb5)
+		seq_printf(m, ": %s", krb5->name);
+}
+
+static struct rxgk_context *rxgk_get_key(struct rxrpc_connection *conn,
+					 u16 *specific_key_number)
+{
+	refcount_inc(&conn->rxgk.keys[0]->usage);
+	return conn->rxgk.keys[0];
+}
+
+/*
+ * initialise connection security
+ */
+static int rxgk_init_connection_security(struct rxrpc_connection *conn,
+					 struct rxrpc_key_token *token)
+{
+	struct rxgk_context *gk;
+	int ret;
+
+	_enter("{%d},{%x}", conn->debug_id, key_serial(conn->key));
+
+	conn->security_ix = token->security_index;
+	conn->security_level = token->rxgk->level;
+
+	if (rxrpc_conn_is_client(conn)) {
+		conn->rxgk.start_time = ktime_get();
+		do_div(conn->rxgk.start_time, 100);
+	}
+
+	gk = rxgk_generate_transport_key(conn, token->rxgk, 0, GFP_NOFS);
+	if (IS_ERR(gk))
+		return PTR_ERR(gk);
+	conn->rxgk.keys[0] = gk;
+
+	switch (conn->security_level) {
+	case RXRPC_SECURITY_PLAIN:
+	case RXRPC_SECURITY_AUTH:
+	case RXRPC_SECURITY_ENCRYPT:
+		break;
+	default:
+		ret = -EKEYREJECTED;
+		goto error;
+	}
+
+	ret = 0;
+error:
+	_leave(" = %d", ret);
+	return ret;
+}
+
+/*
+ * Clean up the crypto on a call.
+ */
+static void rxgk_free_call_crypto(struct rxrpc_call *call)
+{
+}
+
+/*
+ * Work out how much data we can put in a packet.
+ */
+static struct rxrpc_txbuf *rxgk_alloc_txbuf(struct rxrpc_call *call, size_t remain, gfp_t gfp)
+{
+	enum krb5_crypto_mode mode;
+	struct rxgk_context *gk;
+	struct rxrpc_txbuf *txb;
+	size_t shdr, alloc, limit, part, offset, gap;
+
+	switch (call->conn->security_level) {
+	default:
+		alloc = umin(remain, RXRPC_JUMBO_DATALEN);
+		return rxrpc_alloc_data_txbuf(call, alloc, 1, gfp);
+	case RXRPC_SECURITY_AUTH:
+		shdr = 0;
+		mode = KRB5_CHECKSUM_MODE;
+		break;
+	case RXRPC_SECURITY_ENCRYPT:
+		shdr = sizeof(struct rxgk_header);
+		mode = KRB5_ENCRYPT_MODE;
+		break;
+	}
+
+	gk = rxgk_get_key(call->conn, NULL);
+	if (IS_ERR(gk))
+		return NULL;
+
+	/* Work out the maximum amount of data that will fit. */
+	alloc = RXRPC_JUMBO_DATALEN;
+	limit = crypto_krb5_how_much_data(gk->krb5, mode, &alloc, &offset);
+
+	if (remain < limit - shdr) {
+		part = remain;
+		alloc = crypto_krb5_how_much_buffer(gk->krb5, mode,
+						    shdr + part, &offset);
+		gap = 0;
+	} else {
+		part = limit - shdr;
+		gap = RXRPC_JUMBO_DATALEN - alloc;
+		alloc = RXRPC_JUMBO_DATALEN;
+	}
+
+	rxgk_put(gk);
+
+	txb = rxrpc_alloc_data_txbuf(call, alloc, 16, gfp);
+	if (!txb)
+		return NULL;
+
+	txb->crypto_header	= offset;
+	txb->sec_header		= shdr;
+	txb->offset		+= offset + shdr;
+	txb->space		= part;
+
+	/* Clear excess space in the packet */
+	if (gap) {
+		struct rxrpc_wire_header *whdr = txb->kvec[0].iov_base;
+		void *p = whdr + 1;
+
+		memset(p + alloc - gap, 0, gap);
+	}
+	return txb;
+}
+
+/*
+ * Integrity mode (sign a packet - level 1 security)
+ */
+static int rxgk_secure_packet_integrity(const struct rxrpc_call *call,
+					struct rxgk_context *gk,
+					struct rxrpc_txbuf *txb)
+{
+	struct rxrpc_wire_header *whdr = txb->kvec[0].iov_base;
+	struct rxgk_header *hdr;
+	struct scatterlist sg[1];
+	struct krb5_buffer metadata;
+	void *payload = whdr + 1;
+	int ret = -ENOMEM;
+
+	_enter("");
+
+	hdr = kzalloc(sizeof(*hdr), GFP_NOFS);
+	if (!hdr)
+		goto error_gk;
+
+	hdr->epoch	= htonl(call->conn->proto.epoch);
+	hdr->cid	= htonl(call->cid);
+	hdr->call_number = htonl(call->call_id);
+	hdr->seq	= htonl(txb->seq);
+	hdr->sec_index	= htonl(call->security_ix);
+	hdr->data_len	= htonl(txb->len);
+	metadata.len = sizeof(*hdr);
+	metadata.data = hdr;
+
+	sg_init_table(sg, 1);
+	sg_set_buf(&sg[0], payload, txb->alloc_size);
+
+	ret = crypto_krb5_get_mic(gk->krb5, gk->tx_Kc, &metadata,
+				  sg, 1, txb->alloc_size,
+				  txb->crypto_header, txb->sec_header + txb->len);
+	if (ret >= 0) {
+		txb->pkt_len = ret;
+		if (txb->alloc_size == RXRPC_JUMBO_DATALEN)
+			txb->jumboable = true;
+		gk->bytes_remaining -= ret;
+	}
+	kfree(hdr);
+error_gk:
+	rxgk_put(gk);
+	_leave(" = %d", ret);
+	return ret;
+}
+
+/*
+ * wholly encrypt a packet (level 2 security)
+ */
+static int rxgk_secure_packet_encrypted(const struct rxrpc_call *call,
+					struct rxgk_context *gk,
+					struct rxrpc_txbuf *txb)
+{
+	struct rxrpc_wire_header *whdr = txb->kvec[0].iov_base;
+	struct rxgk_header *hdr;
+	struct scatterlist sg[1];
+	void *payload = whdr + 1;
+	int ret;
+
+	_enter("%x", txb->len);
+
+	/* Insert the header into the buffer. */
+	hdr = payload + txb->crypto_header;
+	hdr->epoch	 = htonl(call->conn->proto.epoch);
+	hdr->cid	 = htonl(call->cid);
+	hdr->call_number = htonl(call->call_id);
+	hdr->seq	 = htonl(txb->seq);
+	hdr->sec_index	 = htonl(call->security_ix);
+	hdr->data_len	 = htonl(txb->len);
+
+	sg_init_table(sg, 1);
+	sg_set_buf(&sg[0], payload, txb->alloc_size);
+
+	ret = crypto_krb5_encrypt(gk->krb5, gk->tx_enc,
+				  sg, 1, txb->alloc_size,
+				  txb->crypto_header, txb->sec_header + txb->len,
+				  false);
+	if (ret >= 0) {
+		txb->pkt_len = ret;
+		if (txb->alloc_size == RXRPC_JUMBO_DATALEN)
+			txb->jumboable = true;
+		gk->bytes_remaining -= ret;
+	}
+
+	rxgk_put(gk);
+	_leave(" = %d", ret);
+	return ret;
+}
+
+/*
+ * checksum an RxRPC packet header
+ */
+static int rxgk_secure_packet(struct rxrpc_call *call, struct rxrpc_txbuf *txb)
+{
+	struct rxgk_context *gk;
+	int ret;
+
+	_enter("{%d{%x}},{#%u},%u,",
+	       call->debug_id, key_serial(call->conn->key), txb->seq, txb->len);
+
+	gk = rxgk_get_key(call->conn, NULL);
+	if (IS_ERR(gk))
+		return PTR_ERR(gk) == -ESTALE ? -EKEYREJECTED : PTR_ERR(gk);
+
+	ret = key_validate(call->conn->key);
+	if (ret < 0)
+		return ret;
+
+	txb->cksum = htons(gk->key_number);
+
+	switch (call->conn->security_level) {
+	case RXRPC_SECURITY_PLAIN:
+		rxgk_put(gk);
+		txb->pkt_len = txb->len;
+		return 0;
+	case RXRPC_SECURITY_AUTH:
+		return rxgk_secure_packet_integrity(call, gk, txb);
+	case RXRPC_SECURITY_ENCRYPT:
+		return rxgk_secure_packet_encrypted(call, gk, txb);
+	default:
+		rxgk_put(gk);
+		return -EPERM;
+	}
+}
+
+/*
+ * Integrity mode (check the signature on a packet - level 1 security)
+ */
+static int rxgk_verify_packet_integrity(struct rxrpc_call *call,
+					struct rxgk_context *gk,
+					struct sk_buff *skb)
+{
+	struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
+	struct rxgk_header *hdr;
+	struct krb5_buffer metadata;
+	unsigned int offset = sp->offset, len = sp->len;
+	size_t data_offset = 0, data_len = len;
+	u32 ac;
+	int ret = -ENOMEM;
+
+	_enter("");
+
+	crypto_krb5_where_is_the_data(gk->krb5, KRB5_CHECKSUM_MODE,
+				      &data_offset, &data_len);
+
+	hdr = kzalloc(sizeof(*hdr), GFP_NOFS);
+	if (!hdr)
+		return -ENOMEM;
+
+	hdr->epoch	= htonl(call->conn->proto.epoch);
+	hdr->cid	= htonl(call->cid);
+	hdr->call_number = htonl(call->call_id);
+	hdr->seq	= htonl(sp->hdr.seq);
+	hdr->sec_index	= htonl(call->security_ix);
+	hdr->data_len	= htonl(data_len);
+
+	metadata.len = sizeof(*hdr);
+	metadata.data = hdr;
+	ret = rxgk_verify_mic_skb(gk->krb5, gk->rx_Kc, &metadata,
+				  skb, &offset, &len, &ac);
+	kfree(hdr);
+	if (ret == -EPROTO) {
+		rxrpc_abort_eproto(call, skb, ac,
+				   rxgk_abort_1_verify_mic_eproto);
+	} else {
+		sp->offset = offset;
+		sp->len = len;
+	}
+
+	rxgk_put(gk);
+	_leave(" = %d", ret);
+	return ret;
+}
+
+/*
+ * Decrypt an encrypted packet (level 2 security).
+ */
+static int rxgk_verify_packet_encrypted(struct rxrpc_call *call,
+					struct rxgk_context *gk,
+					struct sk_buff *skb)
+{
+	struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
+	struct rxgk_header hdr;
+	unsigned int offset = sp->offset, len = sp->len;
+	int ret;
+	u32 ac;
+
+	_enter("");
+
+	ret = rxgk_decrypt_skb(gk->krb5, gk->rx_enc, skb, &offset, &len, &ac);
+	if (ret == -EPROTO)
+		rxrpc_abort_eproto(call, skb, ac, rxgk_abort_2_decrypt_eproto);
+	if (ret < 0)
+		goto error;
+
+	if (len < sizeof(hdr)) {
+		ret = rxrpc_abort_eproto(call, skb, RXGK_PACKETSHORT,
+					 rxgk_abort_2_short_header);
+		goto error;
+	}
+
+	/* Extract the header from the skb */
+	ret = skb_copy_bits(skb, offset, &hdr, sizeof(hdr));
+	if (ret < 0) {
+		ret = rxrpc_abort_eproto(call, skb, RXGK_PACKETSHORT,
+					 rxgk_abort_2_short_encdata);
+		goto error;
+	}
+	offset += sizeof(hdr);
+	len -= sizeof(hdr);
+
+	if (ntohl(hdr.epoch)		!= call->conn->proto.epoch ||
+	    ntohl(hdr.cid)		!= call->cid ||
+	    ntohl(hdr.call_number)	!= call->call_id ||
+	    ntohl(hdr.seq)		!= sp->hdr.seq ||
+	    ntohl(hdr.sec_index)	!= call->security_ix ||
+	    ntohl(hdr.data_len)		> len) {
+		ret = rxrpc_abort_eproto(call, skb, RXGK_SEALED_INCON,
+					 rxgk_abort_2_short_data);
+		goto error;
+	}
+
+	sp->offset = offset;
+	sp->len = ntohl(hdr.data_len);
+	ret = 0;
+error:
+	rxgk_put(gk);
+	_leave(" = %d", ret);
+	return ret;
+}
+
+/*
+ * Verify the security on a received packet or subpacket (if part of a
+ * jumbo packet).
+ */
+static int rxgk_verify_packet(struct rxrpc_call *call, struct sk_buff *skb)
+{
+	struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
+	struct rxgk_context *gk;
+	u16 key_number = sp->hdr.cksum;
+
+	_enter("{%d{%x}},{#%u}",
+	       call->debug_id, key_serial(call->conn->key), sp->hdr.seq);
+
+	gk = rxgk_get_key(call->conn, &key_number);
+	if (IS_ERR(gk)) {
+		switch (PTR_ERR(gk)) {
+		case -ESTALE:
+			return rxrpc_abort_eproto(call, skb, RXGK_BADKEYNO,
+						  rxgk_abort_bad_key_number);
+		default:
+			return PTR_ERR(gk);
+		}
+	}
+
+	switch (call->conn->security_level) {
+	case RXRPC_SECURITY_PLAIN:
+		return 0;
+	case RXRPC_SECURITY_AUTH:
+		return rxgk_verify_packet_integrity(call, gk, skb);
+	case RXRPC_SECURITY_ENCRYPT:
+		return rxgk_verify_packet_encrypted(call, gk, skb);
+	default:
+		rxgk_put(gk);
+		return -ENOANO;
+	}
+}
+
+/*
+ * Allocate memory to hold a challenge or a response packet.  We're not running
+ * in the io_thread, so we can't use ->tx_alloc.
+ */
+static struct page *rxgk_alloc_packet(size_t total_len)
+{
+	gfp_t gfp = GFP_NOFS;
+	int order;
+
+	order = get_order(total_len);
+	if (order > 0)
+		gfp |= __GFP_COMP;
+	return alloc_pages(gfp, order);
+}
+
+/*
+ * Issue a challenge.
+ */
+static int rxgk_issue_challenge(struct rxrpc_connection *conn)
+{
+	struct rxrpc_wire_header *whdr;
+	struct bio_vec bvec[1];
+	struct msghdr msg;
+	struct page *page;
+	size_t len = sizeof(*whdr) + sizeof(conn->rxgk.nonce);
+	u32 serial;
+	int ret;
+
+	_enter("{%d}", conn->debug_id);
+
+	get_random_bytes(&conn->rxgk.nonce, sizeof(conn->rxgk.nonce));
+
+	/* We can't use conn->tx_alloc without a lock */
+	page = rxgk_alloc_packet(sizeof(*whdr) + sizeof(conn->rxgk.nonce));
+	if (!page)
+		return -ENOMEM;
+
+	bvec[0].bv_page	= page;
+	bvec[0].bv_len	= len;
+	iov_iter_bvec(&msg.msg_iter, WRITE, bvec, 1, len);
+
+	msg.msg_name	= &conn->peer->srx.transport;
+	msg.msg_namelen	= conn->peer->srx.transport_len;
+	msg.msg_control	= NULL;
+	msg.msg_controllen = 0;
+	msg.msg_flags	= MSG_SPLICE_PAGES;
+
+	whdr = page_address(page);
+	whdr->epoch	= htonl(conn->proto.epoch);
+	whdr->cid	= htonl(conn->proto.cid);
+	whdr->callNumber = 0;
+	whdr->seq	= 0;
+	whdr->type	= RXRPC_PACKET_TYPE_CHALLENGE;
+	whdr->flags	= conn->out_clientflag;
+	whdr->userStatus = 0;
+	whdr->securityIndex = conn->security_ix;
+	whdr->_rsvd	= 0;
+	whdr->serviceId	= htons(conn->service_id);
+
+	memcpy(whdr + 1, conn->rxgk.nonce, sizeof(conn->rxgk.nonce));
+
+	serial = rxrpc_get_next_serials(conn, 1);
+	whdr->serial = htonl(serial);
+
+	ret = do_udp_sendmsg(conn->local->socket, &msg, len);
+	if (ret > 0)
+		conn->peer->last_tx_at = ktime_get_seconds();
+	__free_page(page);
+
+	if (ret < 0) {
+		trace_rxrpc_tx_fail(conn->debug_id, serial, ret,
+				    rxrpc_tx_point_rxgk_challenge);
+		return -EAGAIN;
+	}
+
+	trace_rxrpc_tx_packet(conn->debug_id, whdr,
+			      rxrpc_tx_point_rxgk_challenge);
+	_leave(" = 0");
+	return 0;
+}
+
+/*
+ * Send a response packet.
+ */
+static int rxgk_send_response(struct rxrpc_connection *conn,
+			      struct page *page, size_t total_len)
+{
+	struct rxrpc_wire_header *whdr = page_address(page);
+	struct bio_vec bvec[1];
+	struct msghdr msg;
+	u32 serial;
+	int ret;
+
+	_enter("");
+
+	msg.msg_name	= &conn->peer->srx.transport;
+	msg.msg_namelen	= conn->peer->srx.transport_len;
+	msg.msg_control	= NULL;
+	msg.msg_controllen = 0;
+	msg.msg_flags	= MSG_SPLICE_PAGES;
+
+	bvec[0].bv_page	= page;
+	bvec[0].bv_len	= total_len;
+	iov_iter_bvec(&msg.msg_iter, WRITE, bvec, 1, total_len);
+
+	serial = rxrpc_get_next_serials(conn, 1);
+	whdr->serial = htonl(serial);
+
+	ret = do_udp_sendmsg(conn->local->socket, &msg, total_len);
+	if (ret < 0) {
+		trace_rxrpc_tx_fail(conn->debug_id, serial, ret,
+				    rxrpc_tx_point_rxgk_response);
+		return -EAGAIN;
+	}
+
+	conn->peer->last_tx_at = ktime_get_seconds();
+	_leave(" = 0");
+	return 0;
+}
+
+/*
+ * Construct the authenticator to go in the response packet
+ *
+ * struct RXGK_Authenticator {
+ *	opaque nonce[20];
+ *	opaque appdata<>;
+ *	RXGK_Level level;
+ *	unsigned int epoch;
+ *	unsigned int cid;
+ *	unsigned int call_numbers<>;
+ * };
+ */
+static void rxgk_construct_authenticator(struct rxrpc_connection *conn,
+					 const u8 *nonce, __be32 *xdr)
+{
+	memcpy(xdr, nonce, 20);
+	xdr += 5;
+	*xdr++ = htonl(0); /* appdata len */
+	*xdr++ = htonl(conn->security_level);
+	*xdr++ = htonl(conn->proto.epoch);
+	*xdr++ = htonl(conn->proto.cid);
+	*xdr++ = htonl(4); /* # call_numbers */
+	*xdr++ = htonl(conn->channels[0].call_counter);
+	*xdr++ = htonl(conn->channels[1].call_counter);
+	*xdr++ = htonl(conn->channels[2].call_counter);
+	*xdr   = htonl(conn->channels[3].call_counter);
+}
+
+/*
+ * Construct the response.
+ *
+ * struct RXGK_Response {
+ *	rxgkTime start_time;
+ *	RXGK_Data token;
+ *	opaque authenticator<RXGK_MAXAUTHENTICATOR>
+ * };
+ */
+static int rxgk_construct_response(struct rxrpc_connection *conn,
+				   struct sk_buff *challenge,
+				   const u8 *nonce)
+{
+	struct rxrpc_wire_header *whdr;
+	struct rxrpc_skb_priv *csp = rxrpc_skb(challenge);
+	struct rxgk_context *gk;
+	struct scatterlist sg[1];
+	struct page *page;
+	size_t resp_len, auth_len, authx_len, auth_offset, authx_offset;
+	__be32 *xdr;
+	void *payload;
+	int ret;
+
+	gk = rxgk_get_key(conn, NULL);
+	if (IS_ERR(gk))
+		return PTR_ERR(gk);
+
+	auth_len = 20 + 4 /* appdatalen */ + 12 + (1 + 4) * 4;
+	authx_len = crypto_krb5_how_much_buffer(gk->krb5, KRB5_ENCRYPT_MODE,
+						auth_len, &auth_offset);
+
+	resp_len  = 8;
+	resp_len += 4 + xdr_round_up(gk->key->ticket.len);
+	resp_len += 4 + xdr_round_up(authx_len);
+
+	ret = -ENOMEM;
+	page = rxgk_alloc_packet(sizeof(*whdr) + resp_len);
+	if (!page)
+		goto error_gk;
+
+	whdr = page_address(page);
+	whdr->epoch		= htonl(csp->hdr.epoch);
+	whdr->cid		= htonl(csp->hdr.cid);
+	whdr->callNumber	= 0;
+	whdr->serial		= 0;
+	whdr->seq		= 0;
+	whdr->type		= RXRPC_PACKET_TYPE_RESPONSE;
+	whdr->flags		= conn->out_clientflag;
+	whdr->userStatus	= 0;
+	whdr->securityIndex	= csp->hdr.securityIndex;
+	whdr->cksum		= htons(gk->key_number);
+	whdr->serviceId		= htons(csp->hdr.serviceId);
+
+	payload = whdr + 1;
+	xdr = payload;
+
+	*xdr++ = htonl(upper_32_bits(conn->rxgk.start_time));
+	*xdr++ = htonl(lower_32_bits(conn->rxgk.start_time));
+	*xdr++ = htonl(gk->key->ticket.len);
+	memcpy(xdr, gk->key->ticket.data, xdr_round_up(gk->key->ticket.len));
+	xdr += xdr_round_up(gk->key->ticket.len) / sizeof(*xdr);
+	*xdr++ = htonl(authx_len);
+
+	authx_offset = (void *)xdr - payload;
+	auth_offset += authx_offset;
+
+	xdr = (void *)payload + auth_offset;
+	rxgk_construct_authenticator(conn, nonce, xdr);
+
+	sg_init_table(sg, 1);
+	sg_set_buf(&sg[0], payload + authx_offset, authx_len);
+	ret = crypto_krb5_encrypt(gk->krb5, gk->resp_enc,
+				  sg, 1, authx_len,
+				  auth_offset - authx_offset, auth_len, false);
+	if (ret < 0)
+		goto error;
+
+	ret = rxgk_send_response(conn, page, sizeof(*whdr) + authx_offset + ret);
+error:
+	__free_page(page);
+error_gk:
+	rxgk_put(gk);
+	_leave(" = %d", ret);
+	return ret;
+}
+
+/*
+ * Respond to a challenge packet
+ */
+static int rxgk_respond_to_challenge(struct rxrpc_connection *conn,
+				     struct sk_buff *skb)
+{
+	u8 nonce[20];
+
+	_enter("{%d,%x}", conn->debug_id, key_serial(conn->key));
+
+	if (!conn->key)
+		return rxrpc_abort_conn(conn, skb, RX_PROTOCOL_ERROR, -EPROTO,
+					rxgk_abort_chall_no_key);
+
+	if (key_validate(conn->key) < 0)
+		return rxrpc_abort_conn(conn, skb, RXGK_EXPIRED, -EPROTO,
+					rxgk_abort_chall_key_expired);
+
+	if (skb_copy_bits(skb, sizeof(struct rxrpc_wire_header),
+			  nonce, sizeof(nonce)) < 0)
+		return rxrpc_abort_conn(conn, skb, RXGK_PACKETSHORT, -EPROTO,
+					rxgk_abort_chall_short);
+
+	return rxgk_construct_response(conn, skb, nonce);
+}
+
+/*
+ * Verify the authenticator.
+ *
+ * struct RXGK_Authenticator {
+ *	opaque nonce[20];
+ *	opaque appdata<>;
+ *	RXGK_Level level;
+ *	unsigned int epoch;
+ *	unsigned int cid;
+ *	unsigned int call_numbers<>;
+ * };
+ */
+static int rxgk_do_verify_authenticator(struct rxrpc_connection *conn,
+					const struct krb5_enctype *krb5,
+					struct sk_buff *skb,
+					__be32 *p, __be32 *end)
+{
+	u32 app_len, call_count, level, epoch, cid, i;
+
+	_enter("");
+
+	if (memcmp(p, conn->rxgk.nonce, 20) != 0)
+		return rxrpc_abort_conn(conn, skb, RXGK_NOTAUTH, -EPROTO,
+					rxgk_abort_resp_bad_nonce);
+	p += 20 / sizeof(__be32);
+
+	app_len	= ntohl(*p++);
+	if (app_len > (end - p) * sizeof(__be32))
+		return rxrpc_abort_conn(conn, skb, RXGK_NOTAUTH, -EPROTO,
+					rxgk_abort_resp_short_applen);
+
+	p += xdr_round_up(app_len) / sizeof(__be32);
+	if (end - p < 4)
+		return rxrpc_abort_conn(conn, skb, RXGK_NOTAUTH, -EPROTO,
+					rxgk_abort_resp_short_applen);
+
+	level	= ntohl(*p++);
+	epoch	= ntohl(*p++);
+	cid	= ntohl(*p++);
+	call_count = ntohl(*p++);
+
+	if (level	!= conn->security_level ||
+	    epoch	!= conn->proto.epoch ||
+	    cid		!= conn->proto.cid ||
+	    call_count	> 4)
+		return rxrpc_abort_conn(conn, skb, RXGK_NOTAUTH, -EPROTO,
+					rxgk_abort_resp_bad_param);
+
+	if (end - p < call_count)
+		return rxrpc_abort_conn(conn, skb, RXGK_NOTAUTH, -EPROTO,
+					rxgk_abort_resp_short_call_list);
+
+	for (i = 0; i < call_count; i++) {
+		u32 call_id = ntohl(*p++);
+
+		if (call_id > INT_MAX)
+			return rxrpc_abort_conn(conn, skb, RXGK_NOTAUTH, -EPROTO,
+						rxgk_abort_resp_bad_callid);
+
+		if (call_id < conn->channels[i].call_counter)
+			return rxrpc_abort_conn(conn, skb, RXGK_NOTAUTH, -EPROTO,
+						rxgk_abort_resp_call_ctr);
+
+		if (call_id > conn->channels[i].call_counter) {
+			if (conn->channels[i].call)
+				return rxrpc_abort_conn(conn, skb, RXGK_NOTAUTH, -EPROTO,
+							rxgk_abort_resp_call_state);
+
+			conn->channels[i].call_counter = call_id;
+		}
+	}
+
+	_leave(" = 0");
+	return 0;
+}
+
+/*
+ * Extract the authenticator and verify it.
+ */
+static int rxgk_verify_authenticator(struct rxrpc_connection *conn,
+				     const struct krb5_enctype *krb5,
+				     struct sk_buff *skb,
+				     unsigned int auth_offset, unsigned int auth_len)
+{
+	void *auth;
+	__be32 *p;
+	int ret;
+
+	auth = kmalloc(auth_len, GFP_NOFS);
+	if (!auth)
+		return -ENOMEM;
+
+	ret = skb_copy_bits(skb, auth_offset, auth, auth_len);
+	if (ret < 0) {
+		ret = rxrpc_abort_conn(conn, skb, RXGK_NOTAUTH, -EPROTO,
+				       rxgk_abort_resp_short_auth);
+		goto error;
+	}
+
+	p = auth;
+	ret = rxgk_do_verify_authenticator(conn, krb5, skb, p, p + auth_len);
+error:
+	kfree(auth);
+	return ret;
+}
+
+/*
+ * Verify a response.
+ *
+ * struct RXGK_Response {
+ *	rxgkTime	start_time;
+ *	RXGK_Data	token;
+ *	opaque		authenticator<RXGK_MAXAUTHENTICATOR>
+ * };
+ */
+static int rxgk_verify_response(struct rxrpc_connection *conn,
+				struct sk_buff *skb)
+{
+	const struct krb5_enctype *krb5;
+	struct rxrpc_key_token *token;
+	struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
+	struct rxgk_response rhdr;
+	struct rxgk_context *gk;
+	struct key *key = NULL;
+	unsigned int offset = sizeof(struct rxrpc_wire_header);
+	unsigned int len = skb->len - sizeof(struct rxrpc_wire_header);
+	unsigned int token_offset, token_len;
+	unsigned int auth_offset, auth_len;
+	__be32 xauth_len;
+	int ret, ec;
+
+	_enter("{%d}", conn->debug_id);
+
+	/* Parse the RXGK_Response object */
+	if (sizeof(rhdr) + sizeof(__be32) > len)
+		goto short_packet;
+
+	if (skb_copy_bits(skb, offset, &rhdr, sizeof(rhdr)) < 0)
+		goto short_packet;
+	offset	+= sizeof(rhdr);
+	len	-= sizeof(rhdr);
+
+	token_offset	= offset;
+	token_len	= ntohl(rhdr.token_len);
+	if (xdr_round_up(token_len) + sizeof(__be32) > len)
+		goto short_packet;
+
+	offset	+= xdr_round_up(token_len);
+	len	-= xdr_round_up(token_len);
+
+	if (skb_copy_bits(skb, offset, &xauth_len, sizeof(xauth_len)) < 0)
+		goto short_packet;
+	offset	+= sizeof(xauth_len);
+	len	-= sizeof(xauth_len);
+
+	auth_offset	= offset;
+	auth_len	= ntohl(xauth_len);
+	if (auth_len < len)
+		goto short_packet;
+	if (auth_len & 3)
+		goto inconsistent;
+	if (auth_len < 20 + 9 * 4)
+		goto auth_too_short;
+
+	/* We need to extract and decrypt the token and instantiate a session
+	 * key for it.  This bit, however, is application-specific.  If
+	 * possible, we use a default parser, but we might end up bumping this
+	 * to the app to deal with - which might mean a round trip to
+	 * userspace.
+	 */
+	ret = rxgk_extract_token(conn, skb, token_offset, token_len, &key);
+	if (ret < 0)
+		goto out;
+
+	/* We now have a key instantiated from the decrypted ticket.  We can
+	 * pass this to the application so that they can parse the ticket
+	 * content and we can use the session key it contains to derive the
+	 * keys we need.
+	 *
+	 * Note that we have to switch enctype at this point as the enctype of
+	 * the ticket doesn't necessarily match that of the transport.
+	 */
+	token = key->payload.data[0];
+	conn->security_level = token->rxgk->level;
+	conn->rxgk.start_time = __be64_to_cpu(rhdr.start_time);
+
+	gk = rxgk_generate_transport_key(conn, token->rxgk, sp->hdr.cksum, GFP_NOFS);
+	if (IS_ERR(gk)) {
+		ret = PTR_ERR(gk);
+		goto cant_get_token;
+	}
+
+	krb5 = gk->krb5;
+
+	/* Decrypt, parse and verify the authenticator. */
+	ret = rxgk_decrypt_skb(krb5, gk->resp_enc, skb,
+			       &auth_offset, &auth_len, &ec);
+	if (ret < 0) {
+		rxrpc_abort_conn(conn, skb, RXGK_SEALED_INCON, ret,
+				 rxgk_abort_resp_auth_dec);
+		goto out;
+	}
+
+	ret = rxgk_verify_authenticator(conn, krb5, skb, auth_offset, auth_len);
+	if (ret < 0)
+		goto out;
+
+	conn->key = key;
+	key = NULL;
+	ret = 0;
+out:
+	key_put(key);
+	_leave(" = %d", ret);
+	return ret;
+
+inconsistent:
+	ret = rxrpc_abort_conn(conn, skb, RXGK_INCONSISTENCY, -EPROTO,
+			       rxgk_abort_resp_xdr_align);
+	goto out;
+auth_too_short:
+	ret = rxrpc_abort_conn(conn, skb, RXGK_PACKETSHORT, -EPROTO,
+			       rxgk_abort_resp_short_auth);
+	goto out;
+short_packet:
+	ret = rxrpc_abort_conn(conn, skb, RXGK_PACKETSHORT, -EPROTO,
+			       rxgk_abort_resp_short_packet);
+	goto out;
+
+cant_get_token:
+	switch (ret) {
+	case -ENOMEM:
+		goto temporary_error;
+	case -EINVAL:
+		ret = rxrpc_abort_conn(conn, skb, RXGK_NOTAUTH, -EKEYREJECTED,
+				       rxgk_abort_resp_internal_error);
+		goto out;
+	case -ENOPKG:
+		ret = rxrpc_abort_conn(conn, skb, RXGK_BADETYPE, -EKEYREJECTED,
+				       rxgk_abort_resp_nopkg);
+		goto out;
+	}
+
+temporary_error:
+	/* Ignore the response packet if we got a temporary error such as
+	 * ENOMEM.  We just want to send the challenge again.  Note that we
+	 * also come out this way if the ticket decryption fails.
+	 */
+	goto out;
+}
+
+/*
+ * clear the connection security
+ */
+static void rxgk_clear(struct rxrpc_connection *conn)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(conn->rxgk.keys); i++)
+		rxgk_put(conn->rxgk.keys[i]);
+}
+
+/*
+ * Initialise the RxGK security service.
+ */
+static int rxgk_init(void)
+{
+	return 0;
+}
+
+/*
+ * Clean up the RxGK security service.
+ */
+static void rxgk_exit(void)
+{
+}
+
+/*
+ * RxRPC YFS GSSAPI-based security
+ */
+const struct rxrpc_security rxgk_yfs = {
+	.name				= "yfs-rxgk",
+	.security_index			= RXRPC_SECURITY_YFS_RXGK,
+	.no_key_abort			= RXGK_NOTAUTH,
+	.init				= rxgk_init,
+	.exit				= rxgk_exit,
+	.preparse_server_key		= rxgk_preparse_server_key,
+	.free_preparse_server_key	= rxgk_free_preparse_server_key,
+	.destroy_server_key		= rxgk_destroy_server_key,
+	.describe_server_key		= rxgk_describe_server_key,
+	.init_connection_security	= rxgk_init_connection_security,
+	.alloc_txbuf			= rxgk_alloc_txbuf,
+	.secure_packet			= rxgk_secure_packet,
+	.verify_packet			= rxgk_verify_packet,
+	.free_call_crypto		= rxgk_free_call_crypto,
+	.issue_challenge		= rxgk_issue_challenge,
+	.respond_to_challenge		= rxgk_respond_to_challenge,
+	.verify_response		= rxgk_verify_response,
+	.clear				= rxgk_clear,
+	.default_decode_ticket		= rxgk_yfs_decode_ticket,
+};
diff --git a/net/rxrpc/rxgk_app.c b/net/rxrpc/rxgk_app.c
new file mode 100644
index 000000000000..6ca9d1d3e4c6
--- /dev/null
+++ b/net/rxrpc/rxgk_app.c
@@ -0,0 +1,284 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Application-specific bits for GSSAPI-based RxRPC security
+ *
+ * Copyright (C) 2023 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells at redhat.com)
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/net.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/key-type.h>
+#include "ar-internal.h"
+#include "rxgk_common.h"
+
+/*
+ * Decode a default-style YFS ticket in a response and turn it into an
+ * rxrpc-type key.
+ *
+ * struct rxgk_key {
+ *	afs_uint32	enctype;
+ *	opaque		key<>;
+ * };
+ *
+ * struct RXGK_AuthName {
+ *	afs_int32	kind;
+ *	opaque		data<AUTHDATAMAX>;
+ *	opaque		display<AUTHPRINTABLEMAX>;
+ * };
+ *
+ * struct RXGK_Token {
+ *	rxgk_key		K0;
+ *	RXGK_Level		level;
+ *	rxgkTime		starttime;
+ *	afs_int32		lifetime;
+ *	afs_int32		bytelife;
+ *	rxgkTime		expirationtime;
+ *	struct RXGK_AuthName	identities<>;
+ * };
+ */
+int rxgk_yfs_decode_ticket(struct rxrpc_connection *conn, struct sk_buff *skb,
+			   unsigned int ticket_offset, unsigned int ticket_len,
+			   struct key **_key)
+{
+	struct rxrpc_key_token *token;
+	const struct cred *cred = current_cred(); // TODO - use socket creds
+	struct key *key;
+	size_t pre_ticket_len, payload_len;
+	unsigned int klen, enctype;
+	void *payload, *ticket;
+	__be32 *t, *p, *q, tmp[2];
+	int ret;
+
+	_enter("");
+
+	/* Get the session key length */
+	ret = skb_copy_bits(skb, ticket_offset, tmp, sizeof(tmp));
+	if (ret < 0)
+		return rxrpc_abort_conn(conn, skb, RXGK_INCONSISTENCY, -EPROTO,
+					rxgk_abort_resp_short_yfs_klen);
+	enctype = ntohl(tmp[0]);
+	klen = ntohl(tmp[1]);
+
+	if (klen > ticket_len - 10 * sizeof(__be32))
+		return rxrpc_abort_conn(conn, skb, RXGK_INCONSISTENCY, -EPROTO,
+					rxgk_abort_resp_short_yfs_key);
+
+	pre_ticket_len = ((5 + 14) * sizeof(__be32) +
+			  xdr_round_up(klen) +
+			  sizeof(__be32));
+	payload_len = pre_ticket_len + xdr_round_up(ticket_len);
+
+	payload = kzalloc(payload_len, GFP_NOFS);
+	if (!payload)
+		return -ENOMEM;
+
+	/* We need to fill out the XDR form for a key payload that we can pass
+	 * to add_key().  Start by copying in the ticket so that we can parse
+	 * it.
+	 */
+	ticket = payload + pre_ticket_len;
+	ret = skb_copy_bits(skb, ticket_offset, ticket, ticket_len);
+	if (ret < 0) {
+		ret = rxrpc_abort_conn(conn, skb, RXGK_INCONSISTENCY, -EPROTO,
+				       rxgk_abort_resp_short_yfs_tkt);
+		goto error;
+	}
+
+	/* Fill out the form header. */
+	p = payload;
+	p[0] = htonl(0); /* Flags */
+	p[1] = htonl(1); /* len(cellname) */
+	p[2] = htonl(0x20000000); /* Cellname " " */
+	p[3] = htonl(1); /* #tokens */
+	p[4] = htonl(15 * sizeof(__be32) + xdr_round_up(klen) +
+		     xdr_round_up(ticket_len)); /* Token len */
+
+	/* Now fill in the body.  Most of this we can just scrape directly from
+	 * the ticket.
+	 */
+	t = ticket + sizeof(__be32) * 2 + xdr_round_up(klen);
+	q = payload + 5 * sizeof(__be32);
+	q[0]  = htonl(RXRPC_SECURITY_YFS_RXGK);
+	q[1]  = t[1];		/* begintime - msw */
+	q[2]  = t[2];		/* - lsw */
+	q[3]  = t[5];		/* endtime - msw */
+	q[4]  = t[6];		/* - lsw */
+	q[5]  = 0;		/* level - msw */
+	q[6]  = t[0];		/* - lsw */
+	q[7]  = 0;		/* lifetime - msw */
+	q[8]  = t[3];		/* - lsw */
+	q[9]  = 0;		/* bytelife - msw */
+	q[10] = t[4];		/* - lsw */
+	q[11] = 0;		/* enctype - msw */
+	q[12] = htonl(enctype);	/* - lsw */
+	q[13] = htonl(klen);	/* Key length */
+
+	q += 14;
+
+	memcpy(q, ticket + sizeof(__be32) * 2, klen);
+	q += xdr_round_up(klen) / 4;
+	q[0] = htonl(ticket_len);
+	q++;
+	if (WARN_ON((unsigned long)q != (unsigned long)ticket)) {
+		ret = -EIO;
+		goto error;
+	}
+
+	/* Ticket read in with skb_copy_bits above */
+	q += xdr_round_up(ticket_len) / 4;
+	if (WARN_ON((unsigned long)q - (unsigned long)payload != payload_len)) {
+		ret = -EIO;
+		goto error;
+	}
+
+	/* Now turn that into a key. */
+	key = key_alloc(&key_type_rxrpc, "x",
+			GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, 0, // TODO: Use socket owner
+			KEY_ALLOC_NOT_IN_QUOTA, NULL);
+	if (IS_ERR(key)) {
+		_leave(" = -ENOMEM [alloc %ld]", PTR_ERR(key));
+		goto error;
+	}
+
+	_debug("key %d", key_serial(key));
+
+	ret = key_instantiate_and_link(key, payload, payload_len, NULL, NULL);
+	if (ret < 0)
+		goto error_key;
+
+	token = key->payload.data[0];
+	token->no_leak_key = true;
+	*_key = key;
+	key = NULL;
+	ret = 0;
+	goto error;
+
+error_key:
+	key_put(key);
+error:
+	kfree_sensitive(payload);
+	_leave(" = %d", ret);
+	return ret;
+}
+
+/*
+ * Extract the token and set up a session key from the details.
+ *
+ * struct RXGK_TokenContainer {
+ *	afs_int32	kvno;
+ *	afs_int32	enctype;
+ *	opaque		encrypted_token<>;
+ * };
+ *
+ * [tools.ietf.org/html/draft-wilkinson-afs3-rxgk-afs-08 sec 6.1]
+ */
+int rxgk_extract_token(struct rxrpc_connection *conn, struct sk_buff *skb,
+		       unsigned int token_offset, unsigned int token_len,
+		       struct key **_key)
+{
+	const struct krb5_enctype *krb5;
+	const struct krb5_buffer *server_secret;
+	struct crypto_aead *token_enc = NULL;
+	struct key *server_key;
+	unsigned int ticket_offset, ticket_len;
+	u32 kvno, enctype;
+	int ret, ec;
+
+	struct {
+		__be32 kvno;
+		__be32 enctype;
+		__be32 token_len;
+	} container;
+
+	/* Decode the RXGK_TokenContainer object.  This tells us which server
+	 * key we should be using.  We can then fetch the key, get the secret
+	 * and set up the crypto to extract the token.
+	 */
+	if (skb_copy_bits(skb, token_offset, &container, sizeof(container)) < 0)
+		return rxrpc_abort_conn(conn, skb, RXGK_PACKETSHORT, -EPROTO,
+					rxgk_abort_resp_tok_short);
+
+	kvno		= ntohl(container.kvno);
+	enctype		= ntohl(container.enctype);
+	ticket_len	= ntohl(container.token_len);
+	ticket_offset	= token_offset + sizeof(container);
+
+	if (xdr_round_up(ticket_len) > token_len - 3 * 4)
+		return rxrpc_abort_conn(conn, skb, RXGK_PACKETSHORT, -EPROTO,
+					rxgk_abort_resp_tok_short);
+
+	_debug("KVNO %u", kvno);
+	_debug("ENC  %u", enctype);
+	_debug("TLEN %u", ticket_len);
+
+	server_key = rxrpc_look_up_server_security(conn, skb, kvno, enctype);
+	if (IS_ERR(server_key))
+		goto cant_get_server_key;
+
+	down_read(&server_key->sem);
+	server_secret = (const void *)&server_key->payload.data[2];
+	ret = rxgk_set_up_token_cipher(server_secret, &token_enc, enctype, &krb5, GFP_NOFS);
+	up_read(&server_key->sem);
+	key_put(server_key);
+	if (ret < 0)
+		goto cant_get_token;
+
+	/* We can now decrypt and parse the token/ticket.  This allows us to
+	 * gain access to K0, from which we can derive the transport key and
+	 * thence decode the authenticator.
+	 */
+	ret = rxgk_decrypt_skb(krb5, token_enc, skb,
+			       &ticket_offset, &ticket_len, &ec);
+	crypto_free_aead(token_enc);
+	token_enc = NULL;
+	if (ret < 0)
+		return rxrpc_abort_conn(conn, skb, ec, ret,
+					rxgk_abort_resp_tok_dec);
+
+	ret = conn->security->default_decode_ticket(conn, skb, ticket_offset,
+						    ticket_len, _key);
+	if (ret < 0)
+		goto cant_get_token;
+
+	_leave(" = 0");
+	return ret;
+
+cant_get_server_key:
+	ret = PTR_ERR(server_key);
+	switch (ret) {
+	case -ENOMEM:
+		goto temporary_error;
+	case -ENOKEY:
+	case -EKEYREJECTED:
+	case -EKEYEXPIRED:
+	case -EKEYREVOKED:
+	case -EPERM:
+		return rxrpc_abort_conn(conn, skb, RXGK_BADKEYNO, -EKEYREJECTED,
+					rxgk_abort_resp_tok_nokey);
+	default:
+		return rxrpc_abort_conn(conn, skb, RXGK_NOTAUTH, -EKEYREJECTED,
+					rxgk_abort_resp_tok_keyerr);
+	}
+
+cant_get_token:
+	switch (ret) {
+	case -ENOMEM:
+		goto temporary_error;
+	case -EINVAL:
+		return rxrpc_abort_conn(conn, skb, RXGK_NOTAUTH, -EKEYREJECTED,
+					rxgk_abort_resp_tok_internal_error);
+	case -ENOPKG:
+		return rxrpc_abort_conn(conn, skb, RXGK_BADETYPE, -EKEYREJECTED,
+					rxgk_abort_resp_tok_nopkg);
+	}
+
+temporary_error:
+	/* Ignore the response packet if we got a temporary error such as
+	 * ENOMEM.  We just want to send the challenge again.  Note that we
+	 * also come out this way if the ticket decryption fails.
+	 */
+	return ret;
+}
diff --git a/net/rxrpc/rxgk_common.h b/net/rxrpc/rxgk_common.h
index c07a5386b303..e5becb16fe64 100644
--- a/net/rxrpc/rxgk_common.h
+++ b/net/rxrpc/rxgk_common.h
@@ -33,6 +33,18 @@ struct rxgk_context {
 	struct crypto_aead	*resp_enc;	/* Response packet enc key */
 };
 
+#define xdr_round_up(x) (round_up((x), sizeof(__be32)))
+
+/*
+ * rxgk_app.c
+ */
+int rxgk_yfs_decode_ticket(struct rxrpc_connection *conn, struct sk_buff *skb,
+			   unsigned int ticket_offset, unsigned int ticket_len,
+			   struct key **_key);
+int rxgk_extract_token(struct rxrpc_connection *conn, struct sk_buff *skb,
+		       unsigned int token_offset, unsigned int token_len,
+		       struct key **_key);
+
 /*
  * rxgk_kdf.c
  */
@@ -46,3 +58,83 @@ int rxgk_set_up_token_cipher(const struct krb5_buffer *server_key,
 			     unsigned int enctype,
 			     const struct krb5_enctype **_krb5,
 			     gfp_t gfp);
+
+/*
+ * Apply decryption and checksumming functions to part of an skbuff.  The
+ * offset and length are updated to reflect the actual content of the encrypted
+ * region.
+ */
+static inline
+int rxgk_decrypt_skb(const struct krb5_enctype *krb5,
+		     struct crypto_aead *aead,
+		     struct sk_buff *skb,
+		     unsigned int *_offset, unsigned int *_len,
+		     int *_error_code)
+{
+	struct scatterlist sg[16];
+	size_t offset = 0, len = *_len;
+	int nr_sg, ret;
+
+	sg_init_table(sg, ARRAY_SIZE(sg));
+	nr_sg = skb_to_sgvec(skb, sg, *_offset, len);
+	if (unlikely(nr_sg < 0))
+		return nr_sg;
+
+	ret = crypto_krb5_decrypt(krb5, aead, sg, nr_sg,
+				  &offset, &len);
+	switch (ret) {
+	case 0:
+		*_offset += offset;
+		*_len = len;
+		break;
+	case -EPROTO:
+	case -EBADMSG:
+		*_error_code = RXGK_SEALED_INCON;
+		break;
+	default:
+		break;
+	}
+
+	return ret;
+}
+
+/*
+ * Check the MIC on a region of an skbuff.  The offset and length are updated
+ * to reflect the actual content of the secure region.
+ */
+static inline
+int rxgk_verify_mic_skb(const struct krb5_enctype *krb5,
+			struct crypto_shash *shash,
+			const struct krb5_buffer *metadata,
+			struct sk_buff *skb,
+			unsigned int *_offset, unsigned int *_len,
+			u32 *_error_code)
+{
+	struct scatterlist sg[16];
+	size_t offset = 0, len = *_len;
+	int nr_sg, ret;
+
+	sg_init_table(sg, ARRAY_SIZE(sg));
+	nr_sg = skb_to_sgvec(skb, sg, *_offset, len);
+	if (unlikely(nr_sg < 0))
+		return nr_sg;
+
+	ret = crypto_krb5_verify_mic(krb5, shash, metadata, sg, nr_sg,
+				     &offset, &len);
+	switch (ret) {
+	case 0:
+		*_offset += offset;
+		*_len = len;
+		break;
+	case -EPROTO:
+	case -EBADMSG:
+		*_error_code = RXGK_SEALED_INCON;
+		break;
+	default:
+		break;
+	}
+
+	*_offset += offset;
+	*_len = len;
+	return ret;
+}
diff --git a/net/rxrpc/rxkad.c b/net/rxrpc/rxkad.c
index 62b09d23ec08..773405b55392 100644
--- a/net/rxrpc/rxkad.c
+++ b/net/rxrpc/rxkad.c
@@ -177,8 +177,10 @@ static struct rxrpc_txbuf *rxkad_alloc_txbuf(struct rxrpc_call *call, size_t rem
 	if (!txb)
 		return NULL;
 
-	txb->offset += shdr;
-	txb->space = part;
+	txb->crypto_header	= 0;
+	txb->sec_header		= shdr;
+	txb->offset		+= shdr;
+	txb->space		= part;
 	return txb;
 }
 
diff --git a/net/rxrpc/security.c b/net/rxrpc/security.c
index 9784adc8f275..078d91a6b77f 100644
--- a/net/rxrpc/security.c
+++ b/net/rxrpc/security.c
@@ -20,6 +20,9 @@ static const struct rxrpc_security *rxrpc_security_types[] = {
 #ifdef CONFIG_RXKAD
 	[RXRPC_SECURITY_RXKAD]	= &rxkad,
 #endif
+#ifdef CONFIG_RXGK
+	[RXRPC_SECURITY_YFS_RXGK] = &rxgk_yfs,
+#endif
 };
 
 int __init rxrpc_init_security(void)



From dhowells at redhat.com  Fri Jan 17 10:35:33 2025
From: dhowells at redhat.com (David Howells)
Date: Fri, 17 Jan 2025 18:35:33 +0000
Subject: [RFC PATCH 24/24] rxrpc: rxgk: Implement connection rekeying
In-Reply-To: <20250117183538.881618-1-dhowells@redhat.com>
References: <20250117183538.881618-1-dhowells@redhat.com>
Message-ID: <20250117183538.881618-25-dhowells@redhat.com>

Implement rekeying of connections with the RxGK security class.  This
involves regenerating the keys with a different key number as part of the
input data after a certain amount of time or a certain amount of bytes
encrypted.  Rekeying may be triggered by either end.

The LSW of the key number is inserted into the security-specific field in
the RX header, and we try and expand it to 32-bits to make it last longer.

Signed-off-by: David Howells <dhowells at redhat.com>
cc: Marc Dionne <marc.dionne at auristor.com>
cc: Herbert Xu <herbert at gondor.apana.org.au>
cc: "David S. Miller" <davem at davemloft.net>
cc: Chuck Lever <chuck.lever at oracle.com>
cc: Eric Dumazet <edumazet at google.com>
cc: Jakub Kicinski <kuba at kernel.org>
cc: Paolo Abeni <pabeni at redhat.com>
cc: Simon Horman <horms at kernel.org>
cc: linux-afs at lists.infradead.org
cc: linux-crypto at vger.kernel.org
cc: netdev at vger.kernel.org
---
 net/rxrpc/ar-internal.h |   5 +-
 net/rxrpc/conn_object.c |   1 +
 net/rxrpc/rxgk.c        | 156 ++++++++++++++++++++++++++++++++++++++--
 3 files changed, 155 insertions(+), 7 deletions(-)

diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h
index 736dc6ea20ac..e00f3b0edc98 100644
--- a/net/rxrpc/ar-internal.h
+++ b/net/rxrpc/ar-internal.h
@@ -533,11 +533,14 @@ struct rxrpc_connection {
 			u32	nonce;		/* response re-use preventer */
 		} rxkad;
 		struct {
-			struct rxgk_context *keys[1];
+			struct rxgk_context *keys[4]; /* (Re-)keying buffer */
 			u64	start_time;	/* The start time for TK derivation */
 			u8	nonce[20];	/* Response re-use preventer */
+			u32	key_number;	/* Current key number */
 		} rxgk;
 	};
+	rwlock_t		security_use_lock; /* Security use/modification lock */
+
 	unsigned long		flags;
 	unsigned long		events;
 	unsigned long		idle_timestamp;	/* Time at which last became idle */
diff --git a/net/rxrpc/conn_object.c b/net/rxrpc/conn_object.c
index 7eba4d7d9a38..56459b00266d 100644
--- a/net/rxrpc/conn_object.c
+++ b/net/rxrpc/conn_object.c
@@ -72,6 +72,7 @@ struct rxrpc_connection *rxrpc_alloc_connection(struct rxrpc_net *rxnet,
 		skb_queue_head_init(&conn->rx_queue);
 		conn->rxnet = rxnet;
 		conn->security = &rxrpc_no_security;
+		rwlock_init(&conn->security_use_lock);
 		spin_lock_init(&conn->state_lock);
 		conn->debug_id = atomic_inc_return(&rxrpc_debug_id);
 		conn->idle_timestamp = jiffies;
diff --git a/net/rxrpc/rxgk.c b/net/rxrpc/rxgk.c
index 9c5c616c40cb..367d65aa7731 100644
--- a/net/rxrpc/rxgk.c
+++ b/net/rxrpc/rxgk.c
@@ -76,11 +76,153 @@ static void rxgk_describe_server_key(const struct key *key, struct seq_file *m)
 		seq_printf(m, ": %s", krb5->name);
 }
 
+/*
+ * Handle rekeying the connection when we see our limits overrun or when the
+ * far side decided to rekey.
+ *
+ * Returns a ref on the context if successful or -ESTALE if the key is out of
+ * date.
+ */
+static struct rxgk_context *rxgk_rekey(struct rxrpc_connection *conn,
+				       const u16 *specific_key_number)
+{
+	struct rxgk_context *gk, *dead = NULL;
+	unsigned int key_number, current_key, mask = ARRAY_SIZE(conn->rxgk.keys) - 1;
+	bool crank = false;
+
+	_enter("%d", specific_key_number ? *specific_key_number : -1);
+
+	mutex_lock(&conn->security_lock);
+
+	current_key = conn->rxgk.key_number;
+	if (!specific_key_number) {
+		key_number = current_key;
+	} else {
+		if (*specific_key_number == (u16)current_key)
+			key_number = current_key;
+		else if (*specific_key_number == (u16)(current_key - 1))
+			key_number = current_key - 1;
+		else if (*specific_key_number == (u16)(current_key + 1))
+			goto crank_window;
+		else
+			goto bad_key;
+	}
+
+	gk = conn->rxgk.keys[key_number & mask];
+	if (!gk)
+		goto generate_key;
+	if (!specific_key_number &&
+	    test_bit(RXGK_TK_NEEDS_REKEY, &gk->flags))
+		goto crank_window;
+
+grab:
+	refcount_inc(&gk->usage);
+	mutex_unlock(&conn->security_lock);
+	rxgk_put(dead);
+	return gk;
+
+crank_window:
+	if (current_key == UINT_MAX)
+		goto bad_key;
+	if (current_key + 1 == UINT_MAX)
+		set_bit(RXRPC_CONN_DONT_REUSE, &conn->flags);
+
+	key_number = current_key + 1;
+	if (WARN_ON(conn->rxgk.keys[key_number & mask]))
+		goto bad_key;
+	crank = true;
+
+generate_key:
+	gk = conn->rxgk.keys[current_key & mask];
+	gk = rxgk_generate_transport_key(conn, gk->key, key_number, GFP_NOFS);
+	if (IS_ERR(gk)) {
+		mutex_unlock(&conn->security_lock);
+		return gk;
+	}
+
+	write_lock(&conn->security_use_lock);
+	if (crank) {
+		current_key++;
+		conn->rxgk.key_number = current_key;
+		dead = conn->rxgk.keys[(current_key - 2) & mask];
+		conn->rxgk.keys[(current_key - 2) & mask] = NULL;
+	}
+	conn->rxgk.keys[current_key & mask] = gk;
+	write_unlock(&conn->security_use_lock);
+	goto grab;
+
+bad_key:
+	mutex_unlock(&conn->security_lock);
+	return ERR_PTR(-ESTALE);
+}
+
+/*
+ * Get the specified keying context.
+ *
+ * Returns a ref on the context if successful or -ESTALE if the key is out of
+ * date.
+ */
 static struct rxgk_context *rxgk_get_key(struct rxrpc_connection *conn,
-					 u16 *specific_key_number)
+					 const u16 *specific_key_number)
 {
-	refcount_inc(&conn->rxgk.keys[0]->usage);
-	return conn->rxgk.keys[0];
+	struct rxgk_context *gk;
+	unsigned int key_number, current_key, mask = ARRAY_SIZE(conn->rxgk.keys) - 1;
+
+	_enter("{%u},%d",
+	       conn->rxgk.key_number, specific_key_number ? *specific_key_number : -1);
+
+	read_lock(&conn->security_use_lock);
+
+	current_key = conn->rxgk.key_number;
+	if (!specific_key_number) {
+		key_number = current_key;
+	} else {
+		/* Only the bottom 16 bits of the key number are exposed in the
+		 * header, so we try and keep the upper 16 bits in step.  The
+		 * whole 32 bits are used to generate the TK.
+		 */
+		if (*specific_key_number == (u16)current_key)
+			key_number = current_key;
+		else if (*specific_key_number == (u16)(current_key - 1))
+			key_number = current_key - 1;
+		else if (*specific_key_number == (u16)(current_key + 1))
+			goto rekey;
+		else
+			goto bad_key;
+	}
+
+	gk = conn->rxgk.keys[key_number & mask];
+	if (!gk)
+		goto slow_path;
+	if (!specific_key_number &&
+	    key_number < UINT_MAX) {
+		if (time_after(jiffies, gk->expiry) ||
+		    gk->bytes_remaining < 0) {
+			set_bit(RXGK_TK_NEEDS_REKEY, &gk->flags);
+			goto slow_path;
+		}
+
+		if (test_bit(RXGK_TK_NEEDS_REKEY, &gk->flags))
+			goto slow_path;
+	}
+
+	refcount_inc(&gk->usage);
+	read_unlock(&conn->security_use_lock);
+	return gk;
+
+rekey:
+	_debug("rekey");
+	if (current_key == UINT_MAX)
+		goto bad_key;
+	gk = conn->rxgk.keys[current_key & mask];
+	if (gk)
+		set_bit(RXGK_TK_NEEDS_REKEY, &gk->flags);
+slow_path:
+	read_unlock(&conn->security_use_lock);
+	return rxgk_rekey(conn, specific_key_number);
+bad_key:
+	read_unlock(&conn->security_use_lock);
+	return ERR_PTR(-ESTALE);
 }
 
 /*
@@ -92,7 +234,8 @@ static int rxgk_init_connection_security(struct rxrpc_connection *conn,
 	struct rxgk_context *gk;
 	int ret;
 
-	_enter("{%d},{%x}", conn->debug_id, key_serial(conn->key));
+	_enter("{%d,%u},{%x}",
+	       conn->debug_id, conn->rxgk.key_number, key_serial(conn->key));
 
 	conn->security_ix = token->security_index;
 	conn->security_level = token->rxgk->level;
@@ -102,10 +245,11 @@ static int rxgk_init_connection_security(struct rxrpc_connection *conn,
 		do_div(conn->rxgk.start_time, 100);
 	}
 
-	gk = rxgk_generate_transport_key(conn, token->rxgk, 0, GFP_NOFS);
+	gk = rxgk_generate_transport_key(conn, token->rxgk, conn->rxgk.key_number,
+					 GFP_NOFS);
 	if (IS_ERR(gk))
 		return PTR_ERR(gk);
-	conn->rxgk.keys[0] = gk;
+	conn->rxgk.keys[gk->key_number & 3] = gk;
 
 	switch (conn->security_level) {
 	case RXRPC_SECURITY_PLAIN:



From chuck.lever at oracle.com  Fri Jan 17 10:57:00 2025
From: chuck.lever at oracle.com (Chuck Lever)
Date: Fri, 17 Jan 2025 13:57:00 -0500
Subject: [RFC PATCH 00/24] crypto: Add generic Kerberos library with AEAD
 template for hash-then-crypt
In-Reply-To: <20250117183538.881618-1-dhowells@redhat.com>
References: <20250117183538.881618-1-dhowells@redhat.com>
Message-ID: <62a69db1-0713-4153-958d-df4c9cc7a86e@oracle.com>

On 1/17/25 1:35 PM, David Howells wrote:
> Hi Herbert, Chuck,
> 
> Here's yet another go at a generic Kerberos crypto library in the kernel so
> that I can share code between rxrpc and sunrpc (and cifs?).  I derived some
> of the parts from the sunrpc gss library and added more advanced AES and
> Camellia crypto.
> 
> I added an addition AEAD template type, derived from authenc, that does
> hash-then-crypt rather than crypt-then-hash as authenc does.  I called it
> "krb5enc" for want of a better name.  Possibly I should call it "hashenc"
> or something like that.
> 
> I went back to my previous more library-based approach, but for encrypt
> mode, I replaced the separate skcipher and shash objects with a single
> templated AEAD object - either krb5enc (AES+SHA1 and Camellia) or authenc
> (AES+SHA2).

I'm philosophically in favor of sharing this code. One of the biggest
benefits will be making review/audit easier. Another will be the
opportunities for more hardware acceleration.

But I can't tell if the library API as laid out here will be a good fit
for SunRPC and our kernel XDR implementation until I actually try to use
the API, which I don't have time for right now. The set of enctypes
indeed seems sufficient to support GSS-API Kerberos, and that is a
fundamental requirement for us.

The Linux in-kernel SMB community might be interested in sharing some of
this too, though they seem to have grown their own enctypes. The Cc list
on the thread is already substantial, but they should be included.

I think it would be fine to merge this, as long as the crypto folks are
happy with it, with its current single consumer (RxRPC), and then we can
explore broader sharing.


> Apart from that, things are much as they were previously from the point of
> view of someone using the API.  There's a new pair of functions that, given
> an encoding type, a key and a usage value, will derive the necessary keys
> and return an AEAD or hash handle.  These handles can then be passed to
> operation functions that will do the actual work of performing an
> encryption, a decryption, MIC generation or MIC verification.
> 
> Querying functions are also available to look up an encoding type table by
> kerberos protocol number and to help manage message layout.
> 
> This library has its own self-testing framework that checks more things
> than is possible with the testmgr, including subkey derivation.  It also
> checks things about the output of encrypt + decrypt that testmgr doesn't.
> That said, testmgr is also provisioned with some encryption and
> checksumming tests for Camilla and AES2, though these have to be
> provisioned with the intermediate subkeys rather than the transport key.
> 
> Note that, for purposes of illustration, I've included some rxrpc patches
> that use this interface to implement the rxgk Rx security class.  The
> branch also is based on net-next that carries some rxrpc patches that are a
> prerequisite for this, but the crypto patches don't need it.
> 
> ---
> The patches can be found here also:
> 
> 	http://git.kernel.org/cgit/linux/kernel/git/dhowells/linux-fs.git/log/?h=crypto-krb5
> 
> David
> 
> David Howells (24):
>    crypto/krb5: Add API Documentation
>    crypto/krb5: Add some constants out of sunrpc headers
>    crypto: Add 'krb5enc' hash and cipher AEAD algorithm
>    crypto/krb5: Test manager data
>    crypto/krb5: Implement Kerberos crypto core
>    crypto/krb5: Add an API to query the layout of the crypto section
>    crypto/krb5: Add an API to alloc and prepare a crypto object
>    crypto/krb5: Add an API to perform requests
>    crypto/krb5: Provide infrastructure and key derivation
>    crypto/krb5: Implement the Kerberos5 rfc3961 key derivation
>    crypto/krb5: Provide RFC3961 setkey packaging functions
>    crypto/krb5: Implement the Kerberos5 rfc3961 encrypt and decrypt
>      functions
>    crypto/krb5: Implement the Kerberos5 rfc3961 get_mic and verify_mic
>    crypto/krb5: Implement the AES enctypes from rfc3962
>    crypto/krb5: Implement the AES enctypes from rfc8009
>    crypto/krb5: Implement the AES encrypt/decrypt from rfc8009
>    crypto/krb5: Implement crypto self-testing
>    crypto/krb5: Add the AES self-testing data from rfc8009
>    crypto/krb5: Implement the Camellia enctypes from rfc6803
>    rxrpc: Add the security index for yfs-rxgk
>    rxrpc: Add YFS RxGK (GSSAPI) security class
>    rxrpc: rxgk: Provide infrastructure and key derivation
>    rxrpc: rxgk: Implement the yfs-rxgk security class (GSSAPI)
>    rxrpc: rxgk: Implement connection rekeying
> 
>   Documentation/crypto/index.rst   |    1 +
>   Documentation/crypto/krb5.rst    |  262 +++++++
>   crypto/Kconfig                   |   13 +
>   crypto/Makefile                  |    3 +
>   crypto/krb5/Kconfig              |   26 +
>   crypto/krb5/Makefile             |   18 +
>   crypto/krb5/internal.h           |  257 +++++++
>   crypto/krb5/krb5_api.c           |  452 ++++++++++++
>   crypto/krb5/krb5_kdf.c           |  145 ++++
>   crypto/krb5/rfc3961_simplified.c |  791 ++++++++++++++++++++
>   crypto/krb5/rfc3962_aes.c        |  115 +++
>   crypto/krb5/rfc6803_camellia.c   |  237 ++++++
>   crypto/krb5/rfc8009_aes2.c       |  431 +++++++++++
>   crypto/krb5/selftest.c           |  544 ++++++++++++++
>   crypto/krb5/selftest_data.c      |  291 ++++++++
>   crypto/krb5enc.c                 |  491 +++++++++++++
>   crypto/testmgr.c                 |   16 +
>   crypto/testmgr.h                 |  401 ++++++++++
>   fs/afs/misc.c                    |   13 +
>   include/crypto/krb5.h            |  167 +++++
>   include/keys/rxrpc-type.h        |   17 +
>   include/trace/events/rxrpc.h     |   36 +
>   include/uapi/linux/rxrpc.h       |   17 +
>   net/rxrpc/Kconfig                |   10 +
>   net/rxrpc/Makefile               |    5 +-
>   net/rxrpc/ar-internal.h          |   22 +
>   net/rxrpc/conn_event.c           |    2 +-
>   net/rxrpc/conn_object.c          |    1 +
>   net/rxrpc/key.c                  |  183 +++++
>   net/rxrpc/output.c               |    2 +-
>   net/rxrpc/protocol.h             |   20 +
>   net/rxrpc/rxgk.c                 | 1170 ++++++++++++++++++++++++++++++
>   net/rxrpc/rxgk_app.c             |  284 ++++++++
>   net/rxrpc/rxgk_common.h          |  140 ++++
>   net/rxrpc/rxgk_kdf.c             |  287 ++++++++
>   net/rxrpc/rxkad.c                |    6 +-
>   net/rxrpc/security.c             |    3 +
>   37 files changed, 6874 insertions(+), 5 deletions(-)
>   create mode 100644 Documentation/crypto/krb5.rst
>   create mode 100644 crypto/krb5/Kconfig
>   create mode 100644 crypto/krb5/Makefile
>   create mode 100644 crypto/krb5/internal.h
>   create mode 100644 crypto/krb5/krb5_api.c
>   create mode 100644 crypto/krb5/krb5_kdf.c
>   create mode 100644 crypto/krb5/rfc3961_simplified.c
>   create mode 100644 crypto/krb5/rfc3962_aes.c
>   create mode 100644 crypto/krb5/rfc6803_camellia.c
>   create mode 100644 crypto/krb5/rfc8009_aes2.c
>   create mode 100644 crypto/krb5/selftest.c
>   create mode 100644 crypto/krb5/selftest_data.c
>   create mode 100644 crypto/krb5enc.c
>   create mode 100644 include/crypto/krb5.h
>   create mode 100644 net/rxrpc/rxgk.c
>   create mode 100644 net/rxrpc/rxgk_app.c
>   create mode 100644 net/rxrpc/rxgk_common.h
>   create mode 100644 net/rxrpc/rxgk_kdf.c
> 


-- 
Chuck Lever


From horms at kernel.org  Mon Jan 20 05:57:54 2025
From: horms at kernel.org (Simon Horman)
Date: Mon, 20 Jan 2025 13:57:54 +0000
Subject: [RFC PATCH 03/24] crypto: Add 'krb5enc' hash and cipher AEAD
 algorithm
In-Reply-To: <20250117183538.881618-4-dhowells@redhat.com>
References: <20250117183538.881618-1-dhowells@redhat.com>
 <20250117183538.881618-4-dhowells@redhat.com>
Message-ID: <20250120135754.GX6206@kernel.org>

On Fri, Jan 17, 2025 at 06:35:12PM +0000, David Howells wrote:
> Add an AEAD template that does hash-then-cipher (unlike authenc that does
> cipher-then-hash).  This is required for a number of Kerberos 5 encoding
> types.
> 
> [!] Note that the net/sunrpc/auth_gss/ implementation gets a pair of
> ciphers, one non-CTS and one CTS, using the former to do all the aligned
> blocks and the latter to do the last two blocks if they aren't also
> aligned.  It may be necessary to do this here too for performance reasons -
> but there are considerations both ways:
> 
>  (1) firstly, there is an optimised assembly version of cts(cbc(aes)) on
>      x86_64 that should be used instead of having two ciphers;
> 
>  (2) secondly, none of the hardware offload drivers seem to offer CTS
>      support (Intel QAT does not, for instance).
> 
> However, I don't know if it's possible to query the crypto API to find out
> whether there's an optimised CTS algorithm available.
> 
> Signed-off-by: David Howells <dhowells at redhat.com>

...

> diff --git a/crypto/krb5enc.c b/crypto/krb5enc.c

...

> +static int krb5enc_verify_hash(struct aead_request *req, void *hash)
> +{
> +	struct crypto_aead *krb5enc = crypto_aead_reqtfm(req);
> +	struct aead_instance *inst = aead_alg_instance(krb5enc);
> +	struct krb5enc_instance_ctx *ictx = aead_instance_ctx(inst);
> +	struct krb5enc_request_ctx *areq_ctx = aead_request_ctx(req);
> +	struct ahash_request *ahreq = (void *)(areq_ctx->tail + ictx->reqoff);
> +	unsigned int authsize = crypto_aead_authsize(krb5enc);
> +	u8 *ihash = ahreq->result + authsize;
> +
> +	scatterwalk_map_and_copy(ihash, req->src, ahreq->nbytes, authsize, 0);
> +
> +	if (crypto_memneq(ihash, ahreq->result, authsize))
> +		return -EBADMSG;
> +	return 0;
> +}
> +
> +static void krb5enc_decrypt_hash_done(void *data, int err)
> +{
> +	struct aead_request *req = data;
> +
> +	if (err)
> +		return krb5enc_request_complete(req, err);
> +
> +	err = krb5enc_verify_hash(req, 0);

Hi David,

Sparse complains that the second argument to krb5enc_verify_hash should be
a pointer rather than an integer. So perhaps this would be slightly better
expressed as (completely untested!):

	err = krb5enc_verify_hash(req, NULL);

> +	krb5enc_request_complete(req, err);

...
> +}


From dhowells at redhat.com  Mon Jan 20 06:25:11 2025
From: dhowells at redhat.com (David Howells)
Date: Mon, 20 Jan 2025 14:25:11 +0000
Subject: [RFC PATCH 03/24] crypto: Add 'krb5enc' hash and cipher AEAD algorithm
In-Reply-To: <20250120135754.GX6206@kernel.org>
References: <20250120135754.GX6206@kernel.org> <20250117183538.881618-1-dhowells@redhat.com> <20250117183538.881618-4-dhowells@redhat.com>
Message-ID: <1201143.1737383111@warthog.procyon.org.uk>

Simon Horman <horms at kernel.org> wrote:

> > +static void krb5enc_decrypt_hash_done(void *data, int err)
> > +{
> > +	struct aead_request *req = data;
> > +
> > +	if (err)
> > +		return krb5enc_request_complete(req, err);
> > +
> > +	err = krb5enc_verify_hash(req, 0);
> 
> Hi David,
> 
> Sparse complains that the second argument to krb5enc_verify_hash should be
> a pointer rather than an integer. So perhaps this would be slightly better
> expressed as (completely untested!):
> 
> 	err = krb5enc_verify_hash(req, NULL);

Actually, no.  It should be "ahreq->result + authsize" and
krb5enc_verify_hash() shouldn't calculate ihash, but use its hash parameter.

I wonder if the testmgr driver tests running the algorithms asynchronously...

Thanks,
David



From ebiggers at kernel.org  Mon Jan 20 09:39:37 2025
From: ebiggers at kernel.org (Eric Biggers)
Date: Mon, 20 Jan 2025 09:39:37 -0800
Subject: [RFC PATCH 03/24] crypto: Add 'krb5enc' hash and cipher AEAD
 algorithm
In-Reply-To: <1201143.1737383111@warthog.procyon.org.uk>
References: <20250120135754.GX6206@kernel.org>
 <20250117183538.881618-1-dhowells@redhat.com>
 <20250117183538.881618-4-dhowells@redhat.com>
 <1201143.1737383111@warthog.procyon.org.uk>
Message-ID: <20250120173937.GA2268@sol.localdomain>

On Mon, Jan 20, 2025 at 02:25:11PM +0000, David Howells wrote:
> 
> I wonder if the testmgr driver tests running the algorithms asynchronously...
> 

Multiple requests in parallel, I think you mean?  No, it doesn't, but it should.

- Eric


From dhowells at redhat.com  Mon Jan 20 10:59:40 2025
From: dhowells at redhat.com (David Howells)
Date: Mon, 20 Jan 2025 18:59:40 +0000
Subject: [RFC PATCH 03/24] crypto: Add 'krb5enc' hash and cipher AEAD algorithm
In-Reply-To: <20250120173937.GA2268@sol.localdomain>
References: <20250120173937.GA2268@sol.localdomain> <20250120135754.GX6206@kernel.org> <20250117183538.881618-1-dhowells@redhat.com> <20250117183538.881618-4-dhowells@redhat.com> <1201143.1737383111@warthog.procyon.org.uk>
Message-ID: <1212970.1737399580@warthog.procyon.org.uk>

Eric Biggers <ebiggers at kernel.org> wrote:

> Multiple requests in parallel, I think you mean?  No, it doesn't, but it
> should.

Not so much.  This bug is on the asynchronous path and not tested by my
rxrpc/rxgk code which only exercises the synchronous path.  I haven't tried to
make that asynchronous yet.  I presume testmgr also only tests the sync path.

David



From ebiggers at kernel.org  Mon Jan 20 11:12:50 2025
From: ebiggers at kernel.org (Eric Biggers)
Date: Mon, 20 Jan 2025 11:12:50 -0800
Subject: [RFC PATCH 03/24] crypto: Add 'krb5enc' hash and cipher AEAD
 algorithm
In-Reply-To: <1212970.1737399580@warthog.procyon.org.uk>
References: <20250120173937.GA2268@sol.localdomain>
 <20250120135754.GX6206@kernel.org>
 <20250117183538.881618-1-dhowells@redhat.com>
 <20250117183538.881618-4-dhowells@redhat.com>
 <1201143.1737383111@warthog.procyon.org.uk>
 <1212970.1737399580@warthog.procyon.org.uk>
Message-ID: <20250120191250.GA39025@sol.localdomain>

On Mon, Jan 20, 2025 at 06:59:40PM +0000, David Howells wrote:
> Eric Biggers <ebiggers at kernel.org> wrote:
> 
> > Multiple requests in parallel, I think you mean?  No, it doesn't, but it
> > should.
> 
> Not so much.  This bug is on the asynchronous path and not tested by my
> rxrpc/rxgk code which only exercises the synchronous path.  I haven't tried to
> make that asynchronous yet.  I presume testmgr also only tests the sync path.
> 
> David
> 

I'm not sure I understand your question.  Users of the crypto API can exclude
asynchronous algorithms when selecting one, but the self-tests do not do that.

In any case, why would you need anything to do asynchronous at all here?

- Eric


From dhowells at redhat.com  Mon Jan 20 12:18:14 2025
From: dhowells at redhat.com (David Howells)
Date: Mon, 20 Jan 2025 20:18:14 +0000
Subject: [RFC PATCH 03/24] crypto: Add 'krb5enc' hash and cipher AEAD algorithm
In-Reply-To: <20250120191250.GA39025@sol.localdomain>
References: <20250120191250.GA39025@sol.localdomain> <20250120173937.GA2268@sol.localdomain> <20250120135754.GX6206@kernel.org> <20250117183538.881618-1-dhowells@redhat.com> <20250117183538.881618-4-dhowells@redhat.com> <1201143.1737383111@warthog.procyon.org.uk> <1212970.1737399580@warthog.procyon.org.uk>
Message-ID: <1215834.1737404294@warthog.procyon.org.uk>

Eric Biggers <ebiggers at kernel.org> wrote:

> In any case, why would you need anything to do asynchronous at all here?

Because authenc, which I copied, passes the asynchronocity mode onto the two
algos it runs (one encrypt, one hash).  If authenc is run synchronously, then
the algos are run synchronously and serially; but if authenc is run async,
then the algos are run asynchronously - but they may still have to be run
serially[*] and the second is dispatched from the completion handler of the
first.  So two different paths through the code exist, and rxgk and testmgr
only test the synchronous path.

[*] Because in authenc-compatible encoding types, the output of the encryption
is hashed.  Older krb5 encodings hash the plaintext and the hash generation
and the encrypt can be run in parallel.  For decrypting, the reverse is true;
authenc may be able to do the decrypt and the hash in parallel...  But
parallellisation also requires that the input and output buffers are not the
same.

Anyway.  If it can be done asynchronously, that should probably be tested.

David



From ebiggers at kernel.org  Mon Jan 20 12:47:21 2025
From: ebiggers at kernel.org (Eric Biggers)
Date: Mon, 20 Jan 2025 12:47:21 -0800
Subject: [RFC PATCH 03/24] crypto: Add 'krb5enc' hash and cipher AEAD
 algorithm
In-Reply-To: <1215834.1737404294@warthog.procyon.org.uk>
References: <20250120191250.GA39025@sol.localdomain>
 <20250120173937.GA2268@sol.localdomain>
 <20250120135754.GX6206@kernel.org>
 <20250117183538.881618-1-dhowells@redhat.com>
 <20250117183538.881618-4-dhowells@redhat.com>
 <1201143.1737383111@warthog.procyon.org.uk>
 <1212970.1737399580@warthog.procyon.org.uk>
 <1215834.1737404294@warthog.procyon.org.uk>
Message-ID: <20250120204721.GA53305@sol.localdomain>

On Mon, Jan 20, 2025 at 08:18:14PM +0000, David Howells wrote:
> Eric Biggers <ebiggers at kernel.org> wrote:
> 
> > In any case, why would you need anything to do asynchronous at all here?
> 
> Because authenc, which I copied, passes the asynchronocity mode onto the two
> algos it runs (one encrypt, one hash).  If authenc is run synchronously, then
> the algos are run synchronously and serially; but if authenc is run async,
> then the algos are run asynchronously - but they may still have to be run
> serially[*] and the second is dispatched from the completion handler of the
> first.  So two different paths through the code exist, and rxgk and testmgr
> only test the synchronous path.

No, it goes in the other direction.  The underlying algorithms decide whether
they are asynchronous or not, and that gets passed up.  It sounds like what you
want to do is test your template in the case where the underlying algorithms are
asynchronous.  There is a way to do that by wrapping the underlying algorithms
with cryptd.  For example the following works with gcm:

python3 <<EOF
import socket
s = socket.socket(socket.AF_ALG, 5, 0)
s.bind(("aead", "gcm_base(cryptd(ctr(aes-generic)),cryptd(ghash-generic))"))
EOF

This really should just be thought of as complying with the outdated design of
the crypto API, though.  In practice synchronous is the only case that really
matters.

> [*] Because in authenc-compatible encoding types, the output of the encryption
> is hashed.  Older krb5 encodings hash the plaintext and the hash generation
> and the encrypt can be run in parallel.  For decrypting, the reverse is true;
> authenc may be able to do the decrypt and the hash in parallel...  But
> parallellisation also requires that the input and output buffers are not the
> same.

The right way to optimize cases like that is to interleave the two computations.
Look at how the AES-GCM assembly code interleaves AES-CTR and GHASH for example.
Doing something with async threads is the completely wrong solution here and
would be much slower.  The amount of time needed to process a single message is
simply far too short for multithreading to be appropriate on a per message
basis.

- Eric


From dhowells at redhat.com  Mon Jan 20 15:12:02 2025
From: dhowells at redhat.com (David Howells)
Date: Mon, 20 Jan 2025 23:12:02 +0000
Subject: [RFC PATCH 03/24] crypto: Add 'krb5enc' hash and cipher AEAD algorithm
In-Reply-To: <1201143.1737383111@warthog.procyon.org.uk>
References: <1201143.1737383111@warthog.procyon.org.uk> <20250120135754.GX6206@kernel.org> <20250117183538.881618-1-dhowells@redhat.com> <20250117183538.881618-4-dhowells@redhat.com>
Message-ID: <1434958.1737414722@warthog.procyon.org.uk>

David Howells <dhowells at redhat.com> wrote:

> > Sparse complains that the second argument to krb5enc_verify_hash should be
> > a pointer rather than an integer. So perhaps this would be slightly better
> > expressed as (completely untested!):
> > 
> > 	err = krb5enc_verify_hash(req, NULL);
> 
> Actually, no.  It should be "ahreq->result + authsize" and
> krb5enc_verify_hash() shouldn't calculate ihash, but use its hash parameter.

Ah.  That's wrong also.  I'm going to drop the second parameter and just
calculate the hash pointers directly.

David
---
diff --git a/crypto/krb5enc.c b/crypto/krb5enc.c
index 931387a8ee6f..e5cec47e7e42 100644
--- a/crypto/krb5enc.c
+++ b/crypto/krb5enc.c
@@ -230,7 +230,7 @@ static int krb5enc_encrypt(struct aead_request *req)
 	return krb5enc_dispatch_encrypt(req, aead_request_flags(req));
 }
 
-static int krb5enc_verify_hash(struct aead_request *req, void *hash)
+static int krb5enc_verify_hash(struct aead_request *req)
 {
 	struct crypto_aead *krb5enc = crypto_aead_reqtfm(req);
 	struct aead_instance *inst = aead_alg_instance(krb5enc);
@@ -238,11 +238,12 @@ static int krb5enc_verify_hash(struct aead_request *req, void *hash)
 	struct krb5enc_request_ctx *areq_ctx = aead_request_ctx(req);
 	struct ahash_request *ahreq = (void *)(areq_ctx->tail + ictx->reqoff);
 	unsigned int authsize = crypto_aead_authsize(krb5enc);
-	u8 *ihash = ahreq->result + authsize;
+	u8 *calc_hash = areq_ctx->tail;
+	u8 *msg_hash  = areq_ctx->tail + authsize;
 
-	scatterwalk_map_and_copy(ihash, req->src, ahreq->nbytes, authsize, 0);
+	scatterwalk_map_and_copy(msg_hash, req->src, ahreq->nbytes, authsize, 0);
 
-	if (crypto_memneq(ihash, ahreq->result, authsize))
+	if (crypto_memneq(msg_hash, calc_hash, authsize))
 		return -EBADMSG;
 	return 0;
 }
@@ -254,7 +255,7 @@ static void krb5enc_decrypt_hash_done(void *data, int err)
 	if (err)
 		return krb5enc_request_complete(req, err);
 
-	err = krb5enc_verify_hash(req, 0);
+	err = krb5enc_verify_hash(req);
 	krb5enc_request_complete(req, err);
 }
 
@@ -284,7 +285,7 @@ static int krb5enc_dispatch_decrypt_hash(struct aead_request *req)
 	if (err < 0)
 		return err;
 
-	return krb5enc_verify_hash(req, hash);
+	return krb5enc_verify_hash(req);
 }
 
 /*
@@ -352,7 +353,7 @@ static int krb5enc_init_tfm(struct crypto_aead *tfm)
 	crypto_aead_set_reqsize(
 		tfm,
 		sizeof(struct krb5enc_request_ctx) +
-		ictx->reqoff +
+		ictx->reqoff + /* Space for two checksums */
 		umax(sizeof(struct ahash_request) + crypto_ahash_reqsize(auth),
 		     sizeof(struct skcipher_request) + crypto_skcipher_reqsize(enc)));
 



From gregkh at linuxfoundation.org  Tue Jan 21 09:51:33 2025
From: gregkh at linuxfoundation.org (Greg Kroah-Hartman)
Date: Tue, 21 Jan 2025 18:51:33 +0100
Subject: [PATCH 5.15 021/127] afs: Fix the maximum cell name length
In-Reply-To: <20250121174529.674452028@linuxfoundation.org>
References: <20250121174529.674452028@linuxfoundation.org>
Message-ID: <20250121174530.492325680@linuxfoundation.org>

5.15-stable review patch.  If anyone has any objections, please let me know.

------------------

From: David Howells <dhowells at redhat.com>

[ Upstream commit 8fd56ad6e7c90ac2bddb0741c6b248c8c5d56ac8 ]

The kafs filesystem limits the maximum length of a cell to 256 bytes, but a
problem occurs if someone actually does that: kafs tries to create a
directory under /proc/net/afs/ with the name of the cell, but that fails
with a warning:

        WARNING: CPU: 0 PID: 9 at fs/proc/generic.c:405

because procfs limits the maximum filename length to 255.

However, the DNS limits the maximum lookup length and, by extension, the
maximum cell name, to 255 less two (length count and trailing NUL).

Fix this by limiting the maximum acceptable cellname length to 253.  This
also allows us to be sure we can create the "/afs/.<cell>/" mountpoint too.

Further, split the YFS VL record cell name maximum to be the 256 allowed by
the protocol and ignore the record retrieved by YFSVL.GetCellName if it
exceeds 253.

Fixes: c3e9f888263b ("afs: Implement client support for the YFSVL.GetCellName RPC op")
Reported-by: syzbot+7848fee1f1e5c53f912b at syzkaller.appspotmail.com
Closes: https://lore.kernel.org/r/6776d25d.050a0220.3a8527.0048.GAE at google.com/
Signed-off-by: David Howells <dhowells at redhat.com>
Link: https://lore.kernel.org/r/376236.1736180460 at warthog.procyon.org.uk
Tested-by: syzbot+7848fee1f1e5c53f912b at syzkaller.appspotmail.com
cc: Marc Dionne <marc.dionne at auristor.com>
cc: linux-afs at lists.infradead.org
Signed-off-by: Christian Brauner <brauner at kernel.org>
Signed-off-by: Sasha Levin <sashal at kernel.org>
---
 fs/afs/afs.h      | 2 +-
 fs/afs/afs_vl.h   | 1 +
 fs/afs/vl_alias.c | 8 ++++++--
 fs/afs/vlclient.c | 2 +-
 4 files changed, 9 insertions(+), 4 deletions(-)

diff --git a/fs/afs/afs.h b/fs/afs/afs.h
index 432cb4b23961..3ea5f3e3c922 100644
--- a/fs/afs/afs.h
+++ b/fs/afs/afs.h
@@ -10,7 +10,7 @@
 
 #include <linux/in.h>
 
-#define AFS_MAXCELLNAME		256  	/* Maximum length of a cell name */
+#define AFS_MAXCELLNAME		253  	/* Maximum length of a cell name (DNS limited) */
 #define AFS_MAXVOLNAME		64  	/* Maximum length of a volume name */
 #define AFS_MAXNSERVERS		8   	/* Maximum servers in a basic volume record */
 #define AFS_NMAXNSERVERS	13  	/* Maximum servers in a N/U-class volume record */
diff --git a/fs/afs/afs_vl.h b/fs/afs/afs_vl.h
index 9c65ffb8a523..8da0899fbc08 100644
--- a/fs/afs/afs_vl.h
+++ b/fs/afs/afs_vl.h
@@ -13,6 +13,7 @@
 #define AFS_VL_PORT		7003	/* volume location service port */
 #define VL_SERVICE		52	/* RxRPC service ID for the Volume Location service */
 #define YFS_VL_SERVICE		2503	/* Service ID for AuriStor upgraded VL service */
+#define YFS_VL_MAXCELLNAME	256  	/* Maximum length of a cell name in YFS protocol */
 
 enum AFSVL_Operations {
 	VLGETENTRYBYID		= 503,	/* AFS Get VLDB entry by ID */
diff --git a/fs/afs/vl_alias.c b/fs/afs/vl_alias.c
index f04a80e4f5c3..83cf1bfbe343 100644
--- a/fs/afs/vl_alias.c
+++ b/fs/afs/vl_alias.c
@@ -302,6 +302,7 @@ static char *afs_vl_get_cell_name(struct afs_cell *cell, struct key *key)
 static int yfs_check_canonical_cell_name(struct afs_cell *cell, struct key *key)
 {
 	struct afs_cell *master;
+	size_t name_len;
 	char *cell_name;
 
 	cell_name = afs_vl_get_cell_name(cell, key);
@@ -313,8 +314,11 @@ static int yfs_check_canonical_cell_name(struct afs_cell *cell, struct key *key)
 		return 0;
 	}
 
-	master = afs_lookup_cell(cell->net, cell_name, strlen(cell_name),
-				 NULL, false);
+	name_len = strlen(cell_name);
+	if (!name_len || name_len > AFS_MAXCELLNAME)
+		master = ERR_PTR(-EOPNOTSUPP);
+	else
+		master = afs_lookup_cell(cell->net, cell_name, name_len, NULL, false);
 	kfree(cell_name);
 	if (IS_ERR(master))
 		return PTR_ERR(master);
diff --git a/fs/afs/vlclient.c b/fs/afs/vlclient.c
index 00fca3c66ba6..16653f2ffe4f 100644
--- a/fs/afs/vlclient.c
+++ b/fs/afs/vlclient.c
@@ -671,7 +671,7 @@ static int afs_deliver_yfsvl_get_cell_name(struct afs_call *call)
 			return ret;
 
 		namesz = ntohl(call->tmp);
-		if (namesz > AFS_MAXCELLNAME)
+		if (namesz > YFS_VL_MAXCELLNAME)
 			return afs_protocol_error(call, afs_eproto_cellname_len);
 		paddedsz = (namesz + 3) & ~3;
 		call->count = namesz;
-- 
2.39.5





From gregkh at linuxfoundation.org  Tue Jan 21 09:51:54 2025
From: gregkh at linuxfoundation.org (Greg Kroah-Hartman)
Date: Tue, 21 Jan 2025 18:51:54 +0100
Subject: [PATCH 6.6 28/72] kheaders: Ignore silly-rename files
In-Reply-To: <20250121174523.429119852@linuxfoundation.org>
References: <20250121174523.429119852@linuxfoundation.org>
Message-ID: <20250121174524.509553308@linuxfoundation.org>

6.6-stable review patch.  If anyone has any objections, please let me know.

------------------

From: David Howells <dhowells at redhat.com>

[ Upstream commit 973b710b8821c3401ad7a25360c89e94b26884ac ]

Tell tar to ignore silly-rename files (".__afs*" and ".nfs*") when building
the header archive.  These occur when a file that is open is unlinked
locally, but hasn't yet been closed.  Such files are visible to the user
via the getdents() syscall and so programs may want to do things with them.

During the kernel build, such files may be made during the processing of
header files and the cleanup may get deferred by fput() which may result in
tar seeing these files when it reads the directory, but they may have
disappeared by the time it tries to open them, causing tar to fail with an
error.  Further, we don't want to include them in the tarball if they still
exist.

With CONFIG_HEADERS_INSTALL=y, something like the following may be seen:

   find: './kernel/.tmp_cpio_dir/include/dt-bindings/reset/.__afs2080': No such file or directory
   tar: ./include/linux/greybus/.__afs3C95: File removed before we read it

The find warning doesn't seem to cause a problem.

Fix this by telling tar when called from in gen_kheaders.sh to exclude such
files.  This only affects afs and nfs; cifs uses the Windows Hidden
attribute to prevent the file from being seen.

Signed-off-by: David Howells <dhowells at redhat.com>
Link: https://lore.kernel.org/r/20241213135013.2964079-2-dhowells at redhat.com
cc: Masahiro Yamada <masahiroy at kernel.org>
cc: Marc Dionne <marc.dionne at auristor.com>
cc: linux-afs at lists.infradead.org
cc: linux-nfs at vger.kernel.org
cc: linux-kernel at vger.kernel.org
Signed-off-by: Christian Brauner <brauner at kernel.org>
Signed-off-by: Sasha Levin <sashal at kernel.org>
---
 kernel/gen_kheaders.sh | 1 +
 1 file changed, 1 insertion(+)

diff --git a/kernel/gen_kheaders.sh b/kernel/gen_kheaders.sh
index 383fd43ac6122..7e1340da5acae 100755
--- a/kernel/gen_kheaders.sh
+++ b/kernel/gen_kheaders.sh
@@ -89,6 +89,7 @@ find $cpio_dir -type f -print0 |
 
 # Create archive and try to normalize metadata for reproducibility.
 tar "${KBUILD_BUILD_TIMESTAMP:+--mtime=$KBUILD_BUILD_TIMESTAMP}" \
+    --exclude=".__afs*" --exclude=".nfs*" \
     --owner=0 --group=0 --sort=name --numeric-owner --mode=u=rw,go=r,a+X \
     -I $XZ -cf $tarfile -C $cpio_dir/ . > /dev/null
 
-- 
2.39.5





From gregkh at linuxfoundation.org  Tue Jan 21 09:51:41 2025
From: gregkh at linuxfoundation.org (Greg Kroah-Hartman)
Date: Tue, 21 Jan 2025 18:51:41 +0100
Subject: [PATCH 6.12 053/122] kheaders: Ignore silly-rename files
In-Reply-To: <20250121174532.991109301@linuxfoundation.org>
References: <20250121174532.991109301@linuxfoundation.org>
Message-ID: <20250121174535.034891265@linuxfoundation.org>

6.12-stable review patch.  If anyone has any objections, please let me know.

------------------

From: David Howells <dhowells at redhat.com>

[ Upstream commit 973b710b8821c3401ad7a25360c89e94b26884ac ]

Tell tar to ignore silly-rename files (".__afs*" and ".nfs*") when building
the header archive.  These occur when a file that is open is unlinked
locally, but hasn't yet been closed.  Such files are visible to the user
via the getdents() syscall and so programs may want to do things with them.

During the kernel build, such files may be made during the processing of
header files and the cleanup may get deferred by fput() which may result in
tar seeing these files when it reads the directory, but they may have
disappeared by the time it tries to open them, causing tar to fail with an
error.  Further, we don't want to include them in the tarball if they still
exist.

With CONFIG_HEADERS_INSTALL=y, something like the following may be seen:

   find: './kernel/.tmp_cpio_dir/include/dt-bindings/reset/.__afs2080': No such file or directory
   tar: ./include/linux/greybus/.__afs3C95: File removed before we read it

The find warning doesn't seem to cause a problem.

Fix this by telling tar when called from in gen_kheaders.sh to exclude such
files.  This only affects afs and nfs; cifs uses the Windows Hidden
attribute to prevent the file from being seen.

Signed-off-by: David Howells <dhowells at redhat.com>
Link: https://lore.kernel.org/r/20241213135013.2964079-2-dhowells at redhat.com
cc: Masahiro Yamada <masahiroy at kernel.org>
cc: Marc Dionne <marc.dionne at auristor.com>
cc: linux-afs at lists.infradead.org
cc: linux-nfs at vger.kernel.org
cc: linux-kernel at vger.kernel.org
Signed-off-by: Christian Brauner <brauner at kernel.org>
Signed-off-by: Sasha Levin <sashal at kernel.org>
---
 kernel/gen_kheaders.sh | 1 +
 1 file changed, 1 insertion(+)

diff --git a/kernel/gen_kheaders.sh b/kernel/gen_kheaders.sh
index 383fd43ac6122..7e1340da5acae 100755
--- a/kernel/gen_kheaders.sh
+++ b/kernel/gen_kheaders.sh
@@ -89,6 +89,7 @@ find $cpio_dir -type f -print0 |
 
 # Create archive and try to normalize metadata for reproducibility.
 tar "${KBUILD_BUILD_TIMESTAMP:+--mtime=$KBUILD_BUILD_TIMESTAMP}" \
+    --exclude=".__afs*" --exclude=".nfs*" \
     --owner=0 --group=0 --sort=name --numeric-owner --mode=u=rw,go=r,a+X \
     -I $XZ -cf $tarfile -C $cpio_dir/ . > /dev/null
 
-- 
2.39.5





From gregkh at linuxfoundation.org  Tue Jan 21 09:51:53 2025
From: gregkh at linuxfoundation.org (Greg Kroah-Hartman)
Date: Tue, 21 Jan 2025 18:51:53 +0100
Subject: [PATCH 6.12 065/122] afs: Fix merge preference rule failure condition
In-Reply-To: <20250121174532.991109301@linuxfoundation.org>
References: <20250121174532.991109301@linuxfoundation.org>
Message-ID: <20250121174535.501629770@linuxfoundation.org>

6.12-stable review patch.  If anyone has any objections, please let me know.

------------------

From: Lizhi Xu <lizhi.xu at windriver.com>

[ Upstream commit 17a4fde81d3a7478d97d15304a6d61094a10c2e3 ]

syzbot reported a lock held when returning to userspace[1].  This is
because if argc is less than 0 and the function returns directly, the held
inode lock is not released.

Fix this by store the error in ret and jump to done to clean up instead of
returning directly.

[dh: Modified Lizhi Xu's original patch to make it honour the error code
from afs_split_string()]

[1]
WARNING: lock held when returning to user space!
6.13.0-rc3-syzkaller-00209-g499551201b5f #0 Not tainted
------------------------------------------------
syz-executor133/5823 is leaving the kernel with locks still held!
1 lock held by syz-executor133/5823:
 #0: ffff888071cffc00 (&sb->s_type->i_mutex_key#9){++++}-{4:4}, at: inode_lock include/linux/fs.h:818 [inline]
 #0: ffff888071cffc00 (&sb->s_type->i_mutex_key#9){++++}-{4:4}, at: afs_proc_addr_prefs_write+0x2bb/0x14e0 fs/afs/addr_prefs.c:388

Reported-by: syzbot+76f33569875eb708e575 at syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=76f33569875eb708e575
Signed-off-by: Lizhi Xu <lizhi.xu at windriver.com>
Signed-off-by: David Howells <dhowells at redhat.com>
Link: https://lore.kernel.org/r/20241226012616.2348907-1-lizhi.xu at windriver.com/
Link: https://lore.kernel.org/r/529850.1736261552 at warthog.procyon.org.uk
Tested-by: syzbot+76f33569875eb708e575 at syzkaller.appspotmail.com
cc: Marc Dionne <marc.dionne at auristor.com>
cc: linux-afs at lists.infradead.org
Signed-off-by: Christian Brauner <brauner at kernel.org>
Signed-off-by: Sasha Levin <sashal at kernel.org>
---
 fs/afs/addr_prefs.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/fs/afs/addr_prefs.c b/fs/afs/addr_prefs.c
index a189ff8a5034e..c0384201b8feb 100644
--- a/fs/afs/addr_prefs.c
+++ b/fs/afs/addr_prefs.c
@@ -413,8 +413,10 @@ int afs_proc_addr_prefs_write(struct file *file, char *buf, size_t size)
 
 	do {
 		argc = afs_split_string(&buf, argv, ARRAY_SIZE(argv));
-		if (argc < 0)
-			return argc;
+		if (argc < 0) {
+			ret = argc;
+			goto done;
+		}
 		if (argc < 2)
 			goto inval;
 
-- 
2.39.5





From gregkh at linuxfoundation.org  Tue Jan 21 09:52:21 2025
From: gregkh at linuxfoundation.org (Greg Kroah-Hartman)
Date: Tue, 21 Jan 2025 18:52:21 +0100
Subject: [PATCH 6.1 22/64] kheaders: Ignore silly-rename files
In-Reply-To: <20250121174521.568417761@linuxfoundation.org>
References: <20250121174521.568417761@linuxfoundation.org>
Message-ID: <20250121174522.404249164@linuxfoundation.org>

6.1-stable review patch.  If anyone has any objections, please let me know.

------------------

From: David Howells <dhowells at redhat.com>

[ Upstream commit 973b710b8821c3401ad7a25360c89e94b26884ac ]

Tell tar to ignore silly-rename files (".__afs*" and ".nfs*") when building
the header archive.  These occur when a file that is open is unlinked
locally, but hasn't yet been closed.  Such files are visible to the user
via the getdents() syscall and so programs may want to do things with them.

During the kernel build, such files may be made during the processing of
header files and the cleanup may get deferred by fput() which may result in
tar seeing these files when it reads the directory, but they may have
disappeared by the time it tries to open them, causing tar to fail with an
error.  Further, we don't want to include them in the tarball if they still
exist.

With CONFIG_HEADERS_INSTALL=y, something like the following may be seen:

   find: './kernel/.tmp_cpio_dir/include/dt-bindings/reset/.__afs2080': No such file or directory
   tar: ./include/linux/greybus/.__afs3C95: File removed before we read it

The find warning doesn't seem to cause a problem.

Fix this by telling tar when called from in gen_kheaders.sh to exclude such
files.  This only affects afs and nfs; cifs uses the Windows Hidden
attribute to prevent the file from being seen.

Signed-off-by: David Howells <dhowells at redhat.com>
Link: https://lore.kernel.org/r/20241213135013.2964079-2-dhowells at redhat.com
cc: Masahiro Yamada <masahiroy at kernel.org>
cc: Marc Dionne <marc.dionne at auristor.com>
cc: linux-afs at lists.infradead.org
cc: linux-nfs at vger.kernel.org
cc: linux-kernel at vger.kernel.org
Signed-off-by: Christian Brauner <brauner at kernel.org>
Signed-off-by: Sasha Levin <sashal at kernel.org>
---
 kernel/gen_kheaders.sh | 1 +
 1 file changed, 1 insertion(+)

diff --git a/kernel/gen_kheaders.sh b/kernel/gen_kheaders.sh
index 12bcd08fe79d4..5d506c6e8c0e7 100755
--- a/kernel/gen_kheaders.sh
+++ b/kernel/gen_kheaders.sh
@@ -82,6 +82,7 @@ find $cpio_dir -type f -print0 |
 
 # Create archive and try to normalize metadata for reproducibility.
 tar "${KBUILD_BUILD_TIMESTAMP:+--mtime=$KBUILD_BUILD_TIMESTAMP}" \
+    --exclude=".__afs*" --exclude=".nfs*" \
     --owner=0 --group=0 --sort=name --numeric-owner --mode=u=rw,go=r,a+X \
     -I $XZ -cf $tarfile -C $cpio_dir/ . > /dev/null
 
-- 
2.39.5





From gregkh at linuxfoundation.org  Tue Jan 21 09:52:51 2025
From: gregkh at linuxfoundation.org (Greg Kroah-Hartman)
Date: Tue, 21 Jan 2025 18:52:51 +0100
Subject: [PATCH 5.15 099/127] kheaders: Ignore silly-rename files
In-Reply-To: <20250121174529.674452028@linuxfoundation.org>
References: <20250121174529.674452028@linuxfoundation.org>
Message-ID: <20250121174533.472187950@linuxfoundation.org>

5.15-stable review patch.  If anyone has any objections, please let me know.

------------------

From: David Howells <dhowells at redhat.com>

[ Upstream commit 973b710b8821c3401ad7a25360c89e94b26884ac ]

Tell tar to ignore silly-rename files (".__afs*" and ".nfs*") when building
the header archive.  These occur when a file that is open is unlinked
locally, but hasn't yet been closed.  Such files are visible to the user
via the getdents() syscall and so programs may want to do things with them.

During the kernel build, such files may be made during the processing of
header files and the cleanup may get deferred by fput() which may result in
tar seeing these files when it reads the directory, but they may have
disappeared by the time it tries to open them, causing tar to fail with an
error.  Further, we don't want to include them in the tarball if they still
exist.

With CONFIG_HEADERS_INSTALL=y, something like the following may be seen:

   find: './kernel/.tmp_cpio_dir/include/dt-bindings/reset/.__afs2080': No such file or directory
   tar: ./include/linux/greybus/.__afs3C95: File removed before we read it

The find warning doesn't seem to cause a problem.

Fix this by telling tar when called from in gen_kheaders.sh to exclude such
files.  This only affects afs and nfs; cifs uses the Windows Hidden
attribute to prevent the file from being seen.

Signed-off-by: David Howells <dhowells at redhat.com>
Link: https://lore.kernel.org/r/20241213135013.2964079-2-dhowells at redhat.com
cc: Masahiro Yamada <masahiroy at kernel.org>
cc: Marc Dionne <marc.dionne at auristor.com>
cc: linux-afs at lists.infradead.org
cc: linux-nfs at vger.kernel.org
cc: linux-kernel at vger.kernel.org
Signed-off-by: Christian Brauner <brauner at kernel.org>
Signed-off-by: Sasha Levin <sashal at kernel.org>
---
 kernel/gen_kheaders.sh | 1 +
 1 file changed, 1 insertion(+)

diff --git a/kernel/gen_kheaders.sh b/kernel/gen_kheaders.sh
index c618e37ccea98..1b2b61ca80659 100755
--- a/kernel/gen_kheaders.sh
+++ b/kernel/gen_kheaders.sh
@@ -82,6 +82,7 @@ find $cpio_dir -type f -print0 |
 
 # Create archive and try to normalize metadata for reproducibility.
 tar "${KBUILD_BUILD_TIMESTAMP:+--mtime=$KBUILD_BUILD_TIMESTAMP}" \
+    --exclude=".__afs*" --exclude=".nfs*" \
     --owner=0 --group=0 --sort=name --numeric-owner --mode=u=rw,go=r,a+X \
     -I $XZ -cf $tarfile -C $cpio_dir/ . > /dev/null
 
-- 
2.39.5





From dhowells at redhat.com  Thu Jan 23 00:59:12 2025
From: dhowells at redhat.com (David Howells)
Date: Thu, 23 Jan 2025 08:59:12 +0000
Subject: [PATCH net] rxrpc, afs: Fix peer hash locking vs RCU callback
Message-ID: <2095618.1737622752@warthog.procyon.org.uk>

In its address list, afs now retains pointers to and refs on one or more
rxrpc_peer objects.  The address list is freed under RCU and at this time,
it puts the refs on those peers.

Now, when an rxrpc_peer object runs out of refs, it gets removed from the
peer hash table and, for that, rxrpc has to take a spinlock.  However, it
is now being called from afs's RCU cleanup, which takes place in BH
context - but it is just taking an ordinary spinlock.

The put may also be called from non-BH context, and so there exists the
possibility of deadlock if the BH-based RCU cleanup happens whilst the hash
spinlock is held.  This led to the attached lockdep complaint.

Fix this by changing spinlocks of rxnet->peer_hash_lock back to
BH-disabling locks.

    ================================
    WARNING: inconsistent lock state
    6.13.0-rc5-build2+ #1223 Tainted: G            E
    --------------------------------
    inconsistent {SOFTIRQ-ON-W} -> {IN-SOFTIRQ-W} usage.
    swapper/1/0 [HC0[0]:SC1[1]:HE1:SE0] takes:
    ffff88810babe228 (&rxnet->peer_hash_lock){+.?.}-{3:3}, at: rxrpc_put_peer+0xcb/0x180
    {SOFTIRQ-ON-W} state was registered at:
      mark_usage+0x164/0x180
      __lock_acquire+0x544/0x990
      lock_acquire.part.0+0x103/0x280
      _raw_spin_lock+0x2f/0x40
      rxrpc_peer_keepalive_worker+0x144/0x440
      process_one_work+0x486/0x7c0
      process_scheduled_works+0x73/0x90
      worker_thread+0x1c8/0x2a0
      kthread+0x19b/0x1b0
      ret_from_fork+0x24/0x40
      ret_from_fork_asm+0x1a/0x30
    irq event stamp: 972402
    hardirqs last  enabled at (972402): [<ffffffff8244360e>] _raw_spin_unlock_irqrestore+0x2e/0x50
    hardirqs last disabled at (972401): [<ffffffff82443328>] _raw_spin_lock_irqsave+0x18/0x60
    softirqs last  enabled at (972300): [<ffffffff810ffbbe>] handle_softirqs+0x3ee/0x430
    softirqs last disabled at (972313): [<ffffffff810ffc54>] __irq_exit_rcu+0x44/0x110

    other info that might help us debug this:
     Possible unsafe locking scenario:
           CPU0
           ----
      lock(&rxnet->peer_hash_lock);
      <Interrupt>
        lock(&rxnet->peer_hash_lock);

     *** DEADLOCK ***
    1 lock held by swapper/1/0:
     #0: ffffffff83576be0 (rcu_callback){....}-{0:0}, at: rcu_lock_acquire+0x7/0x30

    stack backtrace:
    CPU: 1 UID: 0 PID: 0 Comm: swapper/1 Tainted: G            E      6.13.0-rc5-build2+ #1223
    Tainted: [E]=UNSIGNED_MODULE
    Hardware name: ASUS All Series/H97-PLUS, BIOS 2306 10/09/2014
    Call Trace:
     <IRQ>
     dump_stack_lvl+0x57/0x80
     print_usage_bug.part.0+0x227/0x240
     valid_state+0x53/0x70
     mark_lock_irq+0xa5/0x2f0
     mark_lock+0xf7/0x170
     mark_usage+0xe1/0x180
     __lock_acquire+0x544/0x990
     lock_acquire.part.0+0x103/0x280
     _raw_spin_lock+0x2f/0x40
     rxrpc_put_peer+0xcb/0x180
     afs_free_addrlist+0x46/0x90 [kafs]
     rcu_do_batch+0x2d2/0x640
     rcu_core+0x2f7/0x350
     handle_softirqs+0x1ee/0x430
     __irq_exit_rcu+0x44/0x110
     irq_exit_rcu+0xa/0x30
     sysvec_apic_timer_interrupt+0x7f/0xa0
     </IRQ>

Fixes: 72904d7b9bfb ("rxrpc, afs: Allow afs to pin rxrpc_peer objects")
Signed-off-by: David Howells <dhowells at redhat.com>
cc: Marc Dionne <marc.dionne at auristor.com>
cc: "David S. Miller" <davem at davemloft.net>
cc: Eric Dumazet <edumazet at google.com>
cc: Jakub Kicinski <kuba at kernel.org>
cc: Paolo Abeni <pabeni at redhat.com>
cc: Simon Horman <horms at kernel.org>
cc: linux-afs at lists.infradead.org
cc: netdev at vger.kernel.org
---
 net/rxrpc/peer_event.c  |   16 ++++++++--------
 net/rxrpc/peer_object.c |   12 ++++++------
 2 files changed, 14 insertions(+), 14 deletions(-)

diff --git a/net/rxrpc/peer_event.c b/net/rxrpc/peer_event.c
index d82e44a3901b..e874c31fa901 100644
--- a/net/rxrpc/peer_event.c
+++ b/net/rxrpc/peer_event.c
@@ -246,7 +246,7 @@ static void rxrpc_peer_keepalive_dispatch(struct rxrpc_net *rxnet,
 	bool use;
 	int slot;
 
-	spin_lock(&rxnet->peer_hash_lock);
+	spin_lock_bh(&rxnet->peer_hash_lock);
 
 	while (!list_empty(collector)) {
 		peer = list_entry(collector->next,
@@ -257,7 +257,7 @@ static void rxrpc_peer_keepalive_dispatch(struct rxrpc_net *rxnet,
 			continue;
 
 		use = __rxrpc_use_local(peer->local, rxrpc_local_use_peer_keepalive);
-		spin_unlock(&rxnet->peer_hash_lock);
+		spin_unlock_bh(&rxnet->peer_hash_lock);
 
 		if (use) {
 			keepalive_at = peer->last_tx_at + RXRPC_KEEPALIVE_TIME;
@@ -277,17 +277,17 @@ static void rxrpc_peer_keepalive_dispatch(struct rxrpc_net *rxnet,
 			 */
 			slot += cursor;
 			slot &= mask;
-			spin_lock(&rxnet->peer_hash_lock);
+			spin_lock_bh(&rxnet->peer_hash_lock);
 			list_add_tail(&peer->keepalive_link,
 				      &rxnet->peer_keepalive[slot & mask]);
-			spin_unlock(&rxnet->peer_hash_lock);
+			spin_unlock_bh(&rxnet->peer_hash_lock);
 			rxrpc_unuse_local(peer->local, rxrpc_local_unuse_peer_keepalive);
 		}
 		rxrpc_put_peer(peer, rxrpc_peer_put_keepalive);
-		spin_lock(&rxnet->peer_hash_lock);
+		spin_lock_bh(&rxnet->peer_hash_lock);
 	}
 
-	spin_unlock(&rxnet->peer_hash_lock);
+	spin_unlock_bh(&rxnet->peer_hash_lock);
 }
 
 /*
@@ -317,7 +317,7 @@ void rxrpc_peer_keepalive_worker(struct work_struct *work)
 	 * second; the bucket at cursor + 1 goes at now + 1s and so
 	 * on...
 	 */
-	spin_lock(&rxnet->peer_hash_lock);
+	spin_lock_bh(&rxnet->peer_hash_lock);
 	list_splice_init(&rxnet->peer_keepalive_new, &collector);
 
 	stop = cursor + ARRAY_SIZE(rxnet->peer_keepalive);
@@ -329,7 +329,7 @@ void rxrpc_peer_keepalive_worker(struct work_struct *work)
 	}
 
 	base = now;
-	spin_unlock(&rxnet->peer_hash_lock);
+	spin_unlock_bh(&rxnet->peer_hash_lock);
 
 	rxnet->peer_keepalive_base = base;
 	rxnet->peer_keepalive_cursor = cursor;
diff --git a/net/rxrpc/peer_object.c b/net/rxrpc/peer_object.c
index e1c63129586b..0fcc87f0409f 100644
--- a/net/rxrpc/peer_object.c
+++ b/net/rxrpc/peer_object.c
@@ -325,10 +325,10 @@ void rxrpc_new_incoming_peer(struct rxrpc_local *local, struct rxrpc_peer *peer)
 	hash_key = rxrpc_peer_hash_key(local, &peer->srx);
 	rxrpc_init_peer(local, peer, hash_key);
 
-	spin_lock(&rxnet->peer_hash_lock);
+	spin_lock_bh(&rxnet->peer_hash_lock);
 	hash_add_rcu(rxnet->peer_hash, &peer->hash_link, hash_key);
 	list_add_tail(&peer->keepalive_link, &rxnet->peer_keepalive_new);
-	spin_unlock(&rxnet->peer_hash_lock);
+	spin_unlock_bh(&rxnet->peer_hash_lock);
 }
 
 /*
@@ -360,7 +360,7 @@ struct rxrpc_peer *rxrpc_lookup_peer(struct rxrpc_local *local,
 			return NULL;
 		}
 
-		spin_lock(&rxnet->peer_hash_lock);
+		spin_lock_bh(&rxnet->peer_hash_lock);
 
 		/* Need to check that we aren't racing with someone else */
 		peer = __rxrpc_lookup_peer_rcu(local, srx, hash_key);
@@ -373,7 +373,7 @@ struct rxrpc_peer *rxrpc_lookup_peer(struct rxrpc_local *local,
 				      &rxnet->peer_keepalive_new);
 		}
 
-		spin_unlock(&rxnet->peer_hash_lock);
+		spin_unlock_bh(&rxnet->peer_hash_lock);
 
 		if (peer)
 			rxrpc_free_peer(candidate);
@@ -423,10 +423,10 @@ static void __rxrpc_put_peer(struct rxrpc_peer *peer)
 
 	ASSERT(hlist_empty(&peer->error_targets));
 
-	spin_lock(&rxnet->peer_hash_lock);
+	spin_lock_bh(&rxnet->peer_hash_lock);
 	hash_del_rcu(&peer->hash_link);
 	list_del_init(&peer->keepalive_link);
-	spin_unlock(&rxnet->peer_hash_lock);
+	spin_unlock_bh(&rxnet->peer_hash_lock);
 
 	rxrpc_free_peer(peer);
 }



From ihor.solodrai at pm.me  Fri Jan 24 09:59:39 2025
From: ihor.solodrai at pm.me (Ihor Solodrai)
Date: Fri, 24 Jan 2025 17:59:39 +0000
Subject: [PATCH v5 27/32] netfs: Change the read result collector to only use one work item
In-Reply-To: <20241216204124.3752367-28-dhowells@redhat.com>
References: <20241216204124.3752367-1-dhowells@redhat.com> <20241216204124.3752367-28-dhowells@redhat.com>
Message-ID: <a7x33d4dnMdGTtRivptq6S1i8btK70SNBP2XyX_xwDAhLvgQoPox6FVBOkifq4eBinfFfbZlIkMZBe3QarlWTxoEtHZwJCZbNKtaqrR7PvI=@pm.me>

On Monday, December 16th, 2024 at 12:41 PM, David Howells <dhowells at redhat.com> wrote:

> Change the way netfslib collects read results to do all the collection for
> a particular read request using a single work item that walks along the
> subrequest queue as subrequests make progress or complete, unlocking folios
> progressively rather than doing the unlock in parallel as parallel requests
> come in.
> 
> The code is remodelled to be more like the write-side code, though only
> using a single stream. This makes it more directly comparable and thus
> easier to duplicate fixes between the two sides.
> 
> This has a number of advantages:
> 
> (1) It's simpler. There doesn't need to be a complex donation mechanism
> to handle mismatches between the size and alignment of subrequests and
> folios. The collector unlocks folios as the subrequests covering each
> complete.
> 
> (2) It should cause less scheduler overhead as there's a single work item
> in play unlocking pages in parallel when a read gets split up into a
> lot of subrequests instead of one per subrequest.
> 
> Whilst the parallellism is nice in theory, in practice, the vast
> majority of loads are sequential reads of the whole file, so
> committing a bunch of threads to unlocking folios out of order doesn't
> help in those cases.
> 
> (3) It should make it easier to implement content decryption. A folio
> cannot be decrypted until all the requests that contribute to it have
> completed - and, again, most loads are sequential and so, most of the
> time, we want to begin decryption sequentially (though it's great if
> the decryption can happen in parallel).
> 
> There is a disadvantage in that we're losing the ability to decrypt and
> unlock things on an as-things-arrive basis which may affect some
> applications.
> 
> Signed-off-by: David Howells dhowells at redhat.com
> 
> cc: Jeff Layton jlayton at kernel.org
> 
> cc: netfs at lists.linux.dev
> cc: linux-fsdevel at vger.kernel.org
> ---
> fs/9p/vfs_addr.c | 3 +-
> fs/afs/dir.c | 8 +-
> fs/ceph/addr.c | 9 +-
> fs/netfs/buffered_read.c | 160 ++++----
> fs/netfs/direct_read.c | 60 +--
> fs/netfs/internal.h | 21 +-
> fs/netfs/main.c | 2 +-
> fs/netfs/objects.c | 34 +-
> fs/netfs/read_collect.c | 716 ++++++++++++++++++++---------------
> fs/netfs/read_pgpriv2.c | 203 ++++------
> fs/netfs/read_retry.c | 207 +++++-----
> fs/netfs/read_single.c | 37 +-
> fs/netfs/write_collect.c | 4 +-
> fs/netfs/write_issue.c | 2 +-
> fs/netfs/write_retry.c | 14 +-
> fs/smb/client/cifssmb.c | 2 +
> fs/smb/client/smb2pdu.c | 5 +-
> include/linux/netfs.h | 16 +-
> include/trace/events/netfs.h | 79 +---
> 19 files changed, 819 insertions(+), 763 deletions(-)

Hello David.

After recent merge from upstream BPF CI started consistently failing
with a task hanging in v9fs_evict_inode. I bisected the failure to
commit e2d46f2ec332, pointing to this patch.

Reverting the patch seems to have helped:
https://github.com/kernel-patches/vmtest/actions/runs/12952856569

Could you please investigate?

Examples of failed jobs:
  * https://github.com/kernel-patches/bpf/actions/runs/12941732247
  * https://github.com/kernel-patches/bpf/actions/runs/12933849075

A log snippet:

    2025-01-24T02:15:03.9009694Z [  246.932163] INFO: task ip:1055 blocked for more than 122 seconds.
    2025-01-24T02:15:03.9013633Z [  246.932709]       Tainted: G           OE      6.13.0-g2bcb9cf535b8-dirty #149
    2025-01-24T02:15:03.9018791Z [  246.933249] "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
    2025-01-24T02:15:03.9025896Z [  246.933802] task:ip              state:D stack:0     pid:1055  tgid:1055  ppid:1054   flags:0x00004002
    2025-01-24T02:15:03.9028228Z [  246.934564] Call Trace:
    2025-01-24T02:15:03.9029758Z [  246.934764]  <TASK>
    2025-01-24T02:15:03.9032572Z [  246.934937]  __schedule+0xa91/0xe80
    2025-01-24T02:15:03.9035126Z [  246.935224]  schedule+0x41/0xb0
    2025-01-24T02:15:03.9037992Z [  246.935459]  v9fs_evict_inode+0xfe/0x170
    2025-01-24T02:15:03.9041469Z [  246.935748]  ? __pfx_var_wake_function+0x10/0x10
    2025-01-24T02:15:03.9043837Z [  246.936101]  evict+0x1ef/0x360
    2025-01-24T02:15:03.9046624Z [  246.936340]  __dentry_kill+0xb0/0x220
    2025-01-24T02:15:03.9048855Z [  246.936610]  ? dput+0x3a/0x1d0
    2025-01-24T02:15:03.9051128Z [  246.936838]  dput+0x114/0x1d0
    2025-01-24T02:15:03.9053548Z [  246.937069]  __fput+0x136/0x2b0
    2025-01-24T02:15:03.9056154Z [  246.937305]  task_work_run+0x89/0xc0
    2025-01-24T02:15:03.9058593Z [  246.937571]  do_exit+0x2c6/0x9c0
    2025-01-24T02:15:03.9061349Z [  246.937816]  do_group_exit+0xa4/0xb0
    2025-01-24T02:15:03.9064401Z [  246.938090]  __x64_sys_exit_group+0x17/0x20
    2025-01-24T02:15:03.9067235Z [  246.938390]  x64_sys_call+0x21a0/0x21a0
    2025-01-24T02:15:03.9069924Z [  246.938672]  do_syscall_64+0x79/0x120
    2025-01-24T02:15:03.9072746Z [  246.938941]  ? clear_bhb_loop+0x25/0x80
    2025-01-24T02:15:03.9075581Z [  246.939230]  ? clear_bhb_loop+0x25/0x80
    2025-01-24T02:15:03.9079275Z [  246.939510]  entry_SYSCALL_64_after_hwframe+0x76/0x7e
    2025-01-24T02:15:03.9081976Z [  246.939875] RIP: 0033:0x7fb86f66f21d
    2025-01-24T02:15:03.9087533Z [  246.940153] RSP: 002b:00007ffdb3cf93f8 EFLAGS: 00000202 ORIG_RAX: 00000000000000e7
    2025-01-24T02:15:03.9092590Z [  246.940689] RAX: ffffffffffffffda RBX: 00007fb86f785fa8 RCX: 00007fb86f66f21d
    2025-01-24T02:15:03.9097722Z [  246.941201] RDX: 00000000000000e7 RSI: ffffffffffffff80 RDI: 0000000000000000
    2025-01-24T02:15:03.9102762Z [  246.941705] RBP: 00007ffdb3cf9450 R08: 00007ffdb3cf93a0 R09: 0000000000000000
    2025-01-24T02:15:03.9107940Z [  246.942215] R10: 00007ffdb3cf92ff R11: 0000000000000202 R12: 0000000000000001
    2025-01-24T02:15:03.9113002Z [  246.942723] R13: 0000000000000000 R14: 0000000000000000 R15: 00007fb86f785fc0
    2025-01-24T02:15:03.9114614Z [  246.943244]  </TASK>
    2025-01-24T02:15:03.9115895Z [  246.943415]
    2025-01-24T02:15:03.9119326Z [  246.943415] Showing all locks held in the system:
    2025-01-24T02:15:03.9122278Z [  246.943865] 1 lock held by khungtaskd/32:
    2025-01-24T02:15:03.9128640Z [  246.944162]  #0: ffffffffa9195d90 (rcu_read_lock){....}-{1:3}, at: debug_show_all_locks+0x2e/0x180
    2025-01-24T02:15:03.9131426Z [  246.944792] 2 locks held by kworker/0:2/86:
    2025-01-24T02:15:03.9132752Z [  246.945102]
    2025-01-24T02:15:03.9136561Z [  246.945222] =============================================

It's worth noting that that the hanging does not happen on *every*
test run, but often enough to fail the CI pipeline.

You may try reproducing with a container I used for bisection:

    docker pull ghcr.io/theihor/bpf:v9fs_evict_inode-repro
    docker run -d --privileged --device=/dev/kvm --cap-add ALL -v /path/to/your/kernel/source:/ci/workspace ghcr.io/theihor/bpf:v9fs_evict_inode-repro
    docker exec -it <container_id_or_name> /bin/bash
    /ci/run.sh # in the container shell

Note that inside the container it's an "ubuntu" user, and you might
have to run `chown -R ubuntu:ubuntu /ci/workspace` first, or switch to
root.

> [...]



From marc.dionne at auristor.com  Fri Jan 24 10:21:35 2025
From: marc.dionne at auristor.com (Marc Dionne)
Date: Fri, 24 Jan 2025 14:21:35 -0400
Subject: [PATCH v5 27/32] netfs: Change the read result collector to only
 use one work item
In-Reply-To: <a7x33d4dnMdGTtRivptq6S1i8btK70SNBP2XyX_xwDAhLvgQoPox6FVBOkifq4eBinfFfbZlIkMZBe3QarlWTxoEtHZwJCZbNKtaqrR7PvI=@pm.me>
References: <20241216204124.3752367-1-dhowells@redhat.com> <20241216204124.3752367-28-dhowells@redhat.com>
 <a7x33d4dnMdGTtRivptq6S1i8btK70SNBP2XyX_xwDAhLvgQoPox6FVBOkifq4eBinfFfbZlIkMZBe3QarlWTxoEtHZwJCZbNKtaqrR7PvI=@pm.me>
Message-ID: <CAB9dFdtVFgG7OWZRytL9Vpr=knNPnMe6b_Esg7rgfFfwLa8j0A@mail.gmail.com>

On Fri, Jan 24, 2025 at 2:00?PM Ihor Solodrai <ihor.solodrai at pm.me> wrote:
>
> On Monday, December 16th, 2024 at 12:41 PM, David Howells <dhowells at redhat.com> wrote:
>
> > Change the way netfslib collects read results to do all the collection for
> > a particular read request using a single work item that walks along the
> > subrequest queue as subrequests make progress or complete, unlocking folios
> > progressively rather than doing the unlock in parallel as parallel requests
> > come in.
> >
> > The code is remodelled to be more like the write-side code, though only
> > using a single stream. This makes it more directly comparable and thus
> > easier to duplicate fixes between the two sides.
> >
> > This has a number of advantages:
> >
> > (1) It's simpler. There doesn't need to be a complex donation mechanism
> > to handle mismatches between the size and alignment of subrequests and
> > folios. The collector unlocks folios as the subrequests covering each
> > complete.
> >
> > (2) It should cause less scheduler overhead as there's a single work item
> > in play unlocking pages in parallel when a read gets split up into a
> > lot of subrequests instead of one per subrequest.
> >
> > Whilst the parallellism is nice in theory, in practice, the vast
> > majority of loads are sequential reads of the whole file, so
> > committing a bunch of threads to unlocking folios out of order doesn't
> > help in those cases.
> >
> > (3) It should make it easier to implement content decryption. A folio
> > cannot be decrypted until all the requests that contribute to it have
> > completed - and, again, most loads are sequential and so, most of the
> > time, we want to begin decryption sequentially (though it's great if
> > the decryption can happen in parallel).
> >
> > There is a disadvantage in that we're losing the ability to decrypt and
> > unlock things on an as-things-arrive basis which may affect some
> > applications.
> >
> > Signed-off-by: David Howells dhowells at redhat.com
> >
> > cc: Jeff Layton jlayton at kernel.org
> >
> > cc: netfs at lists.linux.dev
> > cc: linux-fsdevel at vger.kernel.org
> > ---
> > fs/9p/vfs_addr.c | 3 +-
> > fs/afs/dir.c | 8 +-
> > fs/ceph/addr.c | 9 +-
> > fs/netfs/buffered_read.c | 160 ++++----
> > fs/netfs/direct_read.c | 60 +--
> > fs/netfs/internal.h | 21 +-
> > fs/netfs/main.c | 2 +-
> > fs/netfs/objects.c | 34 +-
> > fs/netfs/read_collect.c | 716 ++++++++++++++++++++---------------
> > fs/netfs/read_pgpriv2.c | 203 ++++------
> > fs/netfs/read_retry.c | 207 +++++-----
> > fs/netfs/read_single.c | 37 +-
> > fs/netfs/write_collect.c | 4 +-
> > fs/netfs/write_issue.c | 2 +-
> > fs/netfs/write_retry.c | 14 +-
> > fs/smb/client/cifssmb.c | 2 +
> > fs/smb/client/smb2pdu.c | 5 +-
> > include/linux/netfs.h | 16 +-
> > include/trace/events/netfs.h | 79 +---
> > 19 files changed, 819 insertions(+), 763 deletions(-)
>
> Hello David.
>
> After recent merge from upstream BPF CI started consistently failing
> with a task hanging in v9fs_evict_inode. I bisected the failure to
> commit e2d46f2ec332, pointing to this patch.
>
> Reverting the patch seems to have helped:
> https://github.com/kernel-patches/vmtest/actions/runs/12952856569
>
> Could you please investigate?
>
> Examples of failed jobs:
>   * https://github.com/kernel-patches/bpf/actions/runs/12941732247
>   * https://github.com/kernel-patches/bpf/actions/runs/12933849075
>
> A log snippet:
>
>     2025-01-24T02:15:03.9009694Z [  246.932163] INFO: task ip:1055 blocked for more than 122 seconds.
>     2025-01-24T02:15:03.9013633Z [  246.932709]       Tainted: G           OE      6.13.0-g2bcb9cf535b8-dirty #149
>     2025-01-24T02:15:03.9018791Z [  246.933249] "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
>     2025-01-24T02:15:03.9025896Z [  246.933802] task:ip              state:D stack:0     pid:1055  tgid:1055  ppid:1054   flags:0x00004002
>     2025-01-24T02:15:03.9028228Z [  246.934564] Call Trace:
>     2025-01-24T02:15:03.9029758Z [  246.934764]  <TASK>
>     2025-01-24T02:15:03.9032572Z [  246.934937]  __schedule+0xa91/0xe80
>     2025-01-24T02:15:03.9035126Z [  246.935224]  schedule+0x41/0xb0
>     2025-01-24T02:15:03.9037992Z [  246.935459]  v9fs_evict_inode+0xfe/0x170
>     2025-01-24T02:15:03.9041469Z [  246.935748]  ? __pfx_var_wake_function+0x10/0x10
>     2025-01-24T02:15:03.9043837Z [  246.936101]  evict+0x1ef/0x360
>     2025-01-24T02:15:03.9046624Z [  246.936340]  __dentry_kill+0xb0/0x220
>     2025-01-24T02:15:03.9048855Z [  246.936610]  ? dput+0x3a/0x1d0
>     2025-01-24T02:15:03.9051128Z [  246.936838]  dput+0x114/0x1d0
>     2025-01-24T02:15:03.9053548Z [  246.937069]  __fput+0x136/0x2b0
>     2025-01-24T02:15:03.9056154Z [  246.937305]  task_work_run+0x89/0xc0
>     2025-01-24T02:15:03.9058593Z [  246.937571]  do_exit+0x2c6/0x9c0
>     2025-01-24T02:15:03.9061349Z [  246.937816]  do_group_exit+0xa4/0xb0
>     2025-01-24T02:15:03.9064401Z [  246.938090]  __x64_sys_exit_group+0x17/0x20
>     2025-01-24T02:15:03.9067235Z [  246.938390]  x64_sys_call+0x21a0/0x21a0
>     2025-01-24T02:15:03.9069924Z [  246.938672]  do_syscall_64+0x79/0x120
>     2025-01-24T02:15:03.9072746Z [  246.938941]  ? clear_bhb_loop+0x25/0x80
>     2025-01-24T02:15:03.9075581Z [  246.939230]  ? clear_bhb_loop+0x25/0x80
>     2025-01-24T02:15:03.9079275Z [  246.939510]  entry_SYSCALL_64_after_hwframe+0x76/0x7e
>     2025-01-24T02:15:03.9081976Z [  246.939875] RIP: 0033:0x7fb86f66f21d
>     2025-01-24T02:15:03.9087533Z [  246.940153] RSP: 002b:00007ffdb3cf93f8 EFLAGS: 00000202 ORIG_RAX: 00000000000000e7
>     2025-01-24T02:15:03.9092590Z [  246.940689] RAX: ffffffffffffffda RBX: 00007fb86f785fa8 RCX: 00007fb86f66f21d
>     2025-01-24T02:15:03.9097722Z [  246.941201] RDX: 00000000000000e7 RSI: ffffffffffffff80 RDI: 0000000000000000
>     2025-01-24T02:15:03.9102762Z [  246.941705] RBP: 00007ffdb3cf9450 R08: 00007ffdb3cf93a0 R09: 0000000000000000
>     2025-01-24T02:15:03.9107940Z [  246.942215] R10: 00007ffdb3cf92ff R11: 0000000000000202 R12: 0000000000000001
>     2025-01-24T02:15:03.9113002Z [  246.942723] R13: 0000000000000000 R14: 0000000000000000 R15: 00007fb86f785fc0
>     2025-01-24T02:15:03.9114614Z [  246.943244]  </TASK>

That looks very similar to something I saw in afs testing, with a
similar stack but in afs_evict_inode where it hung waiting in
netfs_wait_for_outstanding_io.

David pointed to this bit where there's a double get in
netfs_retry_read_subrequests, since netfs_reissue_read already takes
care of getting a ref on the subrequest:

diff --git a/fs/netfs/read_retry.c b/fs/netfs/read_retry.c
index 2290af0d51ac..53d62e31a4cc 100644
--- a/fs/netfs/read_retry.c
+++ b/fs/netfs/read_retry.c
@@ -152,7 +152,6 @@ static void netfs_retry_read_subrequests(struct
netfs_io_request *rreq)
                                __clear_bit(NETFS_SREQ_BOUNDARY,
&subreq->flags);
                        }

-                       netfs_get_subrequest(subreq,
netfs_sreq_trace_get_resubmit);
                        netfs_reissue_read(rreq, subreq);
                        if (subreq == to)
                                break;

That seems to help for my afs test case, I suspect it might help in
your case as well.

Marc


From ihor.solodrai at pm.me  Fri Jan 24 10:46:11 2025
From: ihor.solodrai at pm.me (Ihor Solodrai)
Date: Fri, 24 Jan 2025 18:46:11 +0000
Subject: [PATCH v5 27/32] netfs: Change the read result collector to only use one work item
In-Reply-To: <CAB9dFdtVFgG7OWZRytL9Vpr=knNPnMe6b_Esg7rgfFfwLa8j0A@mail.gmail.com>
References: <20241216204124.3752367-1-dhowells@redhat.com> <20241216204124.3752367-28-dhowells@redhat.com> <a7x33d4dnMdGTtRivptq6S1i8btK70SNBP2XyX_xwDAhLvgQoPox6FVBOkifq4eBinfFfbZlIkMZBe3QarlWTxoEtHZwJCZbNKtaqrR7PvI=@pm.me> <CAB9dFdtVFgG7OWZRytL9Vpr=knNPnMe6b_Esg7rgfFfwLa8j0A@mail.gmail.com>
Message-ID: <GHG6tQSGPRj9L93-skG-HGz4vGtXUxy6ibsUTKloUKncNmy8A7xgte0MEiI0iZJ7jD-SSrZiK2oswgvJCRan_0ZMi6xDlP11SHDi1Utf7mI=@pm.me>

On Friday, January 24th, 2025 at 10:21 AM, Marc Dionne <marc.dionne at auristor.com> wrote:

> 
> [...]
> 
> > A log snippet:
> > 
> > 2025-01-24T02:15:03.9009694Z [ 246.932163] INFO: task ip:1055 blocked for more than 122 seconds.
> > 2025-01-24T02:15:03.9013633Z [ 246.932709] Tainted: G OE 6.13.0-g2bcb9cf535b8-dirty #149
> > 2025-01-24T02:15:03.9018791Z [ 246.933249] "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
> > 2025-01-24T02:15:03.9025896Z [ 246.933802] task:ip state:D stack:0 pid:1055 tgid:1055 ppid:1054 flags:0x00004002
> > 2025-01-24T02:15:03.9028228Z [ 246.934564] Call Trace:
> > 2025-01-24T02:15:03.9029758Z [ 246.934764] <TASK>
> > 2025-01-24T02:15:03.9032572Z [ 246.934937] __schedule+0xa91/0xe80
> > 2025-01-24T02:15:03.9035126Z [ 246.935224] schedule+0x41/0xb0
> > 2025-01-24T02:15:03.9037992Z [ 246.935459] v9fs_evict_inode+0xfe/0x170
> > 2025-01-24T02:15:03.9041469Z [ 246.935748] ? __pfx_var_wake_function+0x10/0x10
> > 2025-01-24T02:15:03.9043837Z [ 246.936101] evict+0x1ef/0x360
> > 2025-01-24T02:15:03.9046624Z [ 246.936340] __dentry_kill+0xb0/0x220
> > 2025-01-24T02:15:03.9048855Z [ 246.936610] ? dput+0x3a/0x1d0
> > 2025-01-24T02:15:03.9051128Z [ 246.936838] dput+0x114/0x1d0
> > 2025-01-24T02:15:03.9053548Z [ 246.937069] __fput+0x136/0x2b0
> > 2025-01-24T02:15:03.9056154Z [ 246.937305] task_work_run+0x89/0xc0
> > 2025-01-24T02:15:03.9058593Z [ 246.937571] do_exit+0x2c6/0x9c0
> > 2025-01-24T02:15:03.9061349Z [ 246.937816] do_group_exit+0xa4/0xb0
> > 2025-01-24T02:15:03.9064401Z [ 246.938090] __x64_sys_exit_group+0x17/0x20
> > 2025-01-24T02:15:03.9067235Z [ 246.938390] x64_sys_call+0x21a0/0x21a0
> > 2025-01-24T02:15:03.9069924Z [ 246.938672] do_syscall_64+0x79/0x120
> > 2025-01-24T02:15:03.9072746Z [ 246.938941] ? clear_bhb_loop+0x25/0x80
> > 2025-01-24T02:15:03.9075581Z [ 246.939230] ? clear_bhb_loop+0x25/0x80
> > 2025-01-24T02:15:03.9079275Z [ 246.939510] entry_SYSCALL_64_after_hwframe+0x76/0x7e
> > 2025-01-24T02:15:03.9081976Z [ 246.939875] RIP: 0033:0x7fb86f66f21d
> > 2025-01-24T02:15:03.9087533Z [ 246.940153] RSP: 002b:00007ffdb3cf93f8 EFLAGS: 00000202 ORIG_RAX: 00000000000000e7
> > 2025-01-24T02:15:03.9092590Z [ 246.940689] RAX: ffffffffffffffda RBX: 00007fb86f785fa8 RCX: 00007fb86f66f21d
> > 2025-01-24T02:15:03.9097722Z [ 246.941201] RDX: 00000000000000e7 RSI: ffffffffffffff80 RDI: 0000000000000000
> > 2025-01-24T02:15:03.9102762Z [ 246.941705] RBP: 00007ffdb3cf9450 R08: 00007ffdb3cf93a0 R09: 0000000000000000
> > 2025-01-24T02:15:03.9107940Z [ 246.942215] R10: 00007ffdb3cf92ff R11: 0000000000000202 R12: 0000000000000001
> > 2025-01-24T02:15:03.9113002Z [ 246.942723] R13: 0000000000000000 R14: 0000000000000000 R15: 00007fb86f785fc0
> > 2025-01-24T02:15:03.9114614Z [ 246.943244] </TASK>
> 
> 
> That looks very similar to something I saw in afs testing, with a
> similar stack but in afs_evict_inode where it hung waiting in
> netfs_wait_for_outstanding_io.
> 
> David pointed to this bit where there's a double get in
> netfs_retry_read_subrequests, since netfs_reissue_read already takes
> care of getting a ref on the subrequest:
> 
> diff --git a/fs/netfs/read_retry.c b/fs/netfs/read_retry.c
> index 2290af0d51ac..53d62e31a4cc 100644
> --- a/fs/netfs/read_retry.c
> +++ b/fs/netfs/read_retry.c
> @@ -152,7 +152,6 @@ static void netfs_retry_read_subrequests(struct
> netfs_io_request *rreq)
> __clear_bit(NETFS_SREQ_BOUNDARY,
> &subreq->flags);
> 
> }
> 
> - netfs_get_subrequest(subreq,
> netfs_sreq_trace_get_resubmit);
> netfs_reissue_read(rreq, subreq);
> if (subreq == to)
> break;
> 
> That seems to help for my afs test case, I suspect it might help in
> your case as well.

Hi Marc. Thank you for the suggestion.

I've just tried this diff on top of bpf-next (d0d106a2bd21):

diff --git a/fs/netfs/read_retry.c b/fs/netfs/read_retry.c
index 2290af0d51ac..53d62e31a4cc 100644
--- a/fs/netfs/read_retry.c
+++ b/fs/netfs/read_retry.c
@@ -152,7 +152,6 @@ static void netfs_retry_read_subrequests(struct netfs_io_request *rreq)
                                __clear_bit(NETFS_SREQ_BOUNDARY, &subreq->flags);
                        }
 
-                       netfs_get_subrequest(subreq, netfs_sreq_trace_get_resubmit);
                        netfs_reissue_read(rreq, subreq);
                        if (subreq == to)
                                break;


and I'm getting a hung task with the same stack

[  184.362292] INFO: task modprobe:2527 blocked for more than 20 seconds.
[  184.363173]       Tainted: G           OE      6.13.0-gd0d106a2bd21-dirty #1
[  184.363651] "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
[  184.364142] task:modprobe        state:D stack:0     pid:2527  tgid:2527  ppid:2134   flags:0x00000002
[  184.364743] Call Trace:
[  184.364907]  <TASK>
[  184.365057]  __schedule+0xa91/0xe80
[  184.365311]  schedule+0x41/0xb0
[  184.365525]  v9fs_evict_inode+0xfe/0x170
[  184.365782]  ? __pfx_var_wake_function+0x10/0x10
[  184.366082]  evict+0x1ef/0x360
[  184.366312]  __dentry_kill+0xb0/0x220
[  184.366561]  ? dput+0x3a/0x1d0
[  184.366765]  dput+0x114/0x1d0
[  184.366962]  __fput+0x136/0x2b0
[  184.367172]  __x64_sys_close+0x9e/0xd0
[  184.367443]  do_syscall_64+0x79/0x120
[  184.367685]  entry_SYSCALL_64_after_hwframe+0x76/0x7e
[  184.368005] RIP: 0033:0x7f4c6fc7f60b
[  184.368249] RSP: 002b:00007ffc7582beb8 EFLAGS: 00000297 ORIG_RAX: 0000000000000003
[  184.368733] RAX: ffffffffffffffda RBX: 0000555e18cff7a0 RCX: 00007f4c6fc7f60b
[  184.369176] RDX: 00007f4c6fd64ee0 RSI: 0000000000000001 RDI: 0000000000000000
[  184.369634] RBP: 00007ffc7582bee0 R08: 0000000000000000 R09: 0000000000000007
[  184.370078] R10: 0000555e18cff980 R11: 0000000000000297 R12: 0000000000000000
[  184.370544] R13: 00007f4c6fd65030 R14: 0000555e18cff980 R15: 0000555e18d7b750
[  184.371004]  </TASK>
[  184.371151]
[  184.371151] Showing all locks held in the system:
[  184.371560] 1 lock held by khungtaskd/32:
[  184.371816]  #0: ffffffff83195d90 (rcu_read_lock){....}-{1:3}, at: debug_show_all_locks+0x2e/0x180
[  184.372397] 2 locks held by kworker/u8:21/2134:
[  184.372695]  #0: ffff9a5300104d48 ((wq_completion)events_unbound){+.+.}-{0:0}, at: process_scheduled_works+0x23a/0x600
[  184.373376]  #1: ffff9e9882187e20 ((work_completion)(&sub_info->work)){+.+.}-{0:0}, at: process_scheduled_works+0x25a/0x600
[  184.374075]
[  184.374182] =============================================

So this appears to be something different.

> 
> Marc


From marc.c.dionne at gmail.com  Fri Jan 24 11:07:26 2025
From: marc.c.dionne at gmail.com (Marc Dionne)
Date: Fri, 24 Jan 2025 15:07:26 -0400
Subject: [PATCH v5 27/32] netfs: Change the read result collector to only
 use one work item
In-Reply-To: <GHG6tQSGPRj9L93-skG-HGz4vGtXUxy6ibsUTKloUKncNmy8A7xgte0MEiI0iZJ7jD-SSrZiK2oswgvJCRan_0ZMi6xDlP11SHDi1Utf7mI=@pm.me>
References: <20241216204124.3752367-1-dhowells@redhat.com> <20241216204124.3752367-28-dhowells@redhat.com>
 <a7x33d4dnMdGTtRivptq6S1i8btK70SNBP2XyX_xwDAhLvgQoPox6FVBOkifq4eBinfFfbZlIkMZBe3QarlWTxoEtHZwJCZbNKtaqrR7PvI=@pm.me>
 <CAB9dFdtVFgG7OWZRytL9Vpr=knNPnMe6b_Esg7rgfFfwLa8j0A@mail.gmail.com> <GHG6tQSGPRj9L93-skG-HGz4vGtXUxy6ibsUTKloUKncNmy8A7xgte0MEiI0iZJ7jD-SSrZiK2oswgvJCRan_0ZMi6xDlP11SHDi1Utf7mI=@pm.me>
Message-ID: <CAB9dFds_bPG1vThvOxhKcoFbUPGURYRHrY_zubPrAqpQrgHA7A@mail.gmail.com>

On Fri, Jan 24, 2025 at 2:46?PM Ihor Solodrai <ihor.solodrai at pm.me> wrote:
>
> On Friday, January 24th, 2025 at 10:21 AM, Marc Dionne <marc.dionne at auristor.com> wrote:
>
> >
> > [...]
> >
> > > A log snippet:
> > >
> > > 2025-01-24T02:15:03.9009694Z [ 246.932163] INFO: task ip:1055 blocked for more than 122 seconds.
> > > 2025-01-24T02:15:03.9013633Z [ 246.932709] Tainted: G OE 6.13.0-g2bcb9cf535b8-dirty #149
> > > 2025-01-24T02:15:03.9018791Z [ 246.933249] "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
> > > 2025-01-24T02:15:03.9025896Z [ 246.933802] task:ip state:D stack:0 pid:1055 tgid:1055 ppid:1054 flags:0x00004002
> > > 2025-01-24T02:15:03.9028228Z [ 246.934564] Call Trace:
> > > 2025-01-24T02:15:03.9029758Z [ 246.934764] <TASK>
> > > 2025-01-24T02:15:03.9032572Z [ 246.934937] __schedule+0xa91/0xe80
> > > 2025-01-24T02:15:03.9035126Z [ 246.935224] schedule+0x41/0xb0
> > > 2025-01-24T02:15:03.9037992Z [ 246.935459] v9fs_evict_inode+0xfe/0x170
> > > 2025-01-24T02:15:03.9041469Z [ 246.935748] ? __pfx_var_wake_function+0x10/0x10
> > > 2025-01-24T02:15:03.9043837Z [ 246.936101] evict+0x1ef/0x360
> > > 2025-01-24T02:15:03.9046624Z [ 246.936340] __dentry_kill+0xb0/0x220
> > > 2025-01-24T02:15:03.9048855Z [ 246.936610] ? dput+0x3a/0x1d0
> > > 2025-01-24T02:15:03.9051128Z [ 246.936838] dput+0x114/0x1d0
> > > 2025-01-24T02:15:03.9053548Z [ 246.937069] __fput+0x136/0x2b0
> > > 2025-01-24T02:15:03.9056154Z [ 246.937305] task_work_run+0x89/0xc0
> > > 2025-01-24T02:15:03.9058593Z [ 246.937571] do_exit+0x2c6/0x9c0
> > > 2025-01-24T02:15:03.9061349Z [ 246.937816] do_group_exit+0xa4/0xb0
> > > 2025-01-24T02:15:03.9064401Z [ 246.938090] __x64_sys_exit_group+0x17/0x20
> > > 2025-01-24T02:15:03.9067235Z [ 246.938390] x64_sys_call+0x21a0/0x21a0
> > > 2025-01-24T02:15:03.9069924Z [ 246.938672] do_syscall_64+0x79/0x120
> > > 2025-01-24T02:15:03.9072746Z [ 246.938941] ? clear_bhb_loop+0x25/0x80
> > > 2025-01-24T02:15:03.9075581Z [ 246.939230] ? clear_bhb_loop+0x25/0x80
> > > 2025-01-24T02:15:03.9079275Z [ 246.939510] entry_SYSCALL_64_after_hwframe+0x76/0x7e
> > > 2025-01-24T02:15:03.9081976Z [ 246.939875] RIP: 0033:0x7fb86f66f21d
> > > 2025-01-24T02:15:03.9087533Z [ 246.940153] RSP: 002b:00007ffdb3cf93f8 EFLAGS: 00000202 ORIG_RAX: 00000000000000e7
> > > 2025-01-24T02:15:03.9092590Z [ 246.940689] RAX: ffffffffffffffda RBX: 00007fb86f785fa8 RCX: 00007fb86f66f21d
> > > 2025-01-24T02:15:03.9097722Z [ 246.941201] RDX: 00000000000000e7 RSI: ffffffffffffff80 RDI: 0000000000000000
> > > 2025-01-24T02:15:03.9102762Z [ 246.941705] RBP: 00007ffdb3cf9450 R08: 00007ffdb3cf93a0 R09: 0000000000000000
> > > 2025-01-24T02:15:03.9107940Z [ 246.942215] R10: 00007ffdb3cf92ff R11: 0000000000000202 R12: 0000000000000001
> > > 2025-01-24T02:15:03.9113002Z [ 246.942723] R13: 0000000000000000 R14: 0000000000000000 R15: 00007fb86f785fc0
> > > 2025-01-24T02:15:03.9114614Z [ 246.943244] </TASK>
> >
> >
> > That looks very similar to something I saw in afs testing, with a
> > similar stack but in afs_evict_inode where it hung waiting in
> > netfs_wait_for_outstanding_io.
> >
> > David pointed to this bit where there's a double get in
> > netfs_retry_read_subrequests, since netfs_reissue_read already takes
> > care of getting a ref on the subrequest:
> >
> > diff --git a/fs/netfs/read_retry.c b/fs/netfs/read_retry.c
> > index 2290af0d51ac..53d62e31a4cc 100644
> > --- a/fs/netfs/read_retry.c
> > +++ b/fs/netfs/read_retry.c
> > @@ -152,7 +152,6 @@ static void netfs_retry_read_subrequests(struct
> > netfs_io_request *rreq)
> > __clear_bit(NETFS_SREQ_BOUNDARY,
> > &subreq->flags);
> >
> > }
> >
> > - netfs_get_subrequest(subreq,
> > netfs_sreq_trace_get_resubmit);
> > netfs_reissue_read(rreq, subreq);
> > if (subreq == to)
> > break;
> >
> > That seems to help for my afs test case, I suspect it might help in
> > your case as well.
>
> Hi Marc. Thank you for the suggestion.
>
> I've just tried this diff on top of bpf-next (d0d106a2bd21):
>
> diff --git a/fs/netfs/read_retry.c b/fs/netfs/read_retry.c
> index 2290af0d51ac..53d62e31a4cc 100644
> --- a/fs/netfs/read_retry.c
> +++ b/fs/netfs/read_retry.c
> @@ -152,7 +152,6 @@ static void netfs_retry_read_subrequests(struct netfs_io_request *rreq)
>                                 __clear_bit(NETFS_SREQ_BOUNDARY, &subreq->flags);
>                         }
>
> -                       netfs_get_subrequest(subreq, netfs_sreq_trace_get_resubmit);
>                         netfs_reissue_read(rreq, subreq);
>                         if (subreq == to)
>                                 break;
>
>
> and I'm getting a hung task with the same stack
>
> [  184.362292] INFO: task modprobe:2527 blocked for more than 20 seconds.
> [  184.363173]       Tainted: G           OE      6.13.0-gd0d106a2bd21-dirty #1
> [  184.363651] "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
> [  184.364142] task:modprobe        state:D stack:0     pid:2527  tgid:2527  ppid:2134   flags:0x00000002
> [  184.364743] Call Trace:
> [  184.364907]  <TASK>
> [  184.365057]  __schedule+0xa91/0xe80
> [  184.365311]  schedule+0x41/0xb0
> [  184.365525]  v9fs_evict_inode+0xfe/0x170
> [  184.365782]  ? __pfx_var_wake_function+0x10/0x10
> [  184.366082]  evict+0x1ef/0x360
> [  184.366312]  __dentry_kill+0xb0/0x220
> [  184.366561]  ? dput+0x3a/0x1d0
> [  184.366765]  dput+0x114/0x1d0
> [  184.366962]  __fput+0x136/0x2b0
> [  184.367172]  __x64_sys_close+0x9e/0xd0
> [  184.367443]  do_syscall_64+0x79/0x120
> [  184.367685]  entry_SYSCALL_64_after_hwframe+0x76/0x7e
> [  184.368005] RIP: 0033:0x7f4c6fc7f60b
> [  184.368249] RSP: 002b:00007ffc7582beb8 EFLAGS: 00000297 ORIG_RAX: 0000000000000003
> [  184.368733] RAX: ffffffffffffffda RBX: 0000555e18cff7a0 RCX: 00007f4c6fc7f60b
> [  184.369176] RDX: 00007f4c6fd64ee0 RSI: 0000000000000001 RDI: 0000000000000000
> [  184.369634] RBP: 00007ffc7582bee0 R08: 0000000000000000 R09: 0000000000000007
> [  184.370078] R10: 0000555e18cff980 R11: 0000000000000297 R12: 0000000000000000
> [  184.370544] R13: 00007f4c6fd65030 R14: 0000555e18cff980 R15: 0000555e18d7b750
> [  184.371004]  </TASK>
> [  184.371151]
> [  184.371151] Showing all locks held in the system:
> [  184.371560] 1 lock held by khungtaskd/32:
> [  184.371816]  #0: ffffffff83195d90 (rcu_read_lock){....}-{1:3}, at: debug_show_all_locks+0x2e/0x180
> [  184.372397] 2 locks held by kworker/u8:21/2134:
> [  184.372695]  #0: ffff9a5300104d48 ((wq_completion)events_unbound){+.+.}-{0:0}, at: process_scheduled_works+0x23a/0x600
> [  184.373376]  #1: ffff9e9882187e20 ((work_completion)(&sub_info->work)){+.+.}-{0:0}, at: process_scheduled_works+0x25a/0x600
> [  184.374075]
> [  184.374182] =============================================
>
> So this appears to be something different.
>
> >
> > Marc

Looks like there may be a similar issue with the
netfs_get_subrequest() at line 196, which also precedes a call to
netfs_reissue_read.  Might be worth trying with that removed as well.

Marc


From ihor.solodrai at pm.me  Fri Jan 24 11:54:43 2025
From: ihor.solodrai at pm.me (Ihor Solodrai)
Date: Fri, 24 Jan 2025 19:54:43 +0000
Subject: [PATCH v5 27/32] netfs: Change the read result collector to only use one work item
In-Reply-To: <CAB9dFds_bPG1vThvOxhKcoFbUPGURYRHrY_zubPrAqpQrgHA7A@mail.gmail.com>
References: <20241216204124.3752367-1-dhowells@redhat.com> <20241216204124.3752367-28-dhowells@redhat.com> <a7x33d4dnMdGTtRivptq6S1i8btK70SNBP2XyX_xwDAhLvgQoPox6FVBOkifq4eBinfFfbZlIkMZBe3QarlWTxoEtHZwJCZbNKtaqrR7PvI=@pm.me> <CAB9dFdtVFgG7OWZRytL9Vpr=knNPnMe6b_Esg7rgfFfwLa8j0A@mail.gmail.com> <GHG6tQSGPRj9L93-skG-HGz4vGtXUxy6ibsUTKloUKncNmy8A7xgte0MEiI0iZJ7jD-SSrZiK2oswgvJCRan_0ZMi6xDlP11SHDi1Utf7mI=@pm.me> <CAB9dFds_bPG1vThvOxhKcoFbUPGURYRHrY_zubPrAqpQrgHA7A@mail.gmail.com>
Message-ID: <ZWHFHDukzfc7hoFZ8s7ebITzzuXLdL3jtI1Gm7XASpFGprxofyelizx8oA4JZthmee_YlnO7okLF4aUNLXaiZ2Ib_JtUTWzbCcsrnl4LX5w=@pm.me>

On Friday, January 24th, 2025 at 11:07 AM, Marc Dionne <marc.c.dionne at gmail.com> wrote:

> 
> 
> On Fri, Jan 24, 2025 at 2:46?PM Ihor Solodrai ihor.solodrai at pm.me wrote:
> 
> > On Friday, January 24th, 2025 at 10:21 AM, Marc Dionne marc.dionne at auristor.com wrote:
> > 
> > > [...]
> > > 
> > > > A log snippet:
> > > > 
> > > > 2025-01-24T02:15:03.9009694Z [ 246.932163] INFO: task ip:1055 blocked for more than 122 seconds.
> > > > 2025-01-24T02:15:03.9013633Z [ 246.932709] Tainted: G OE 6.13.0-g2bcb9cf535b8-dirty #149
> > > > 2025-01-24T02:15:03.9018791Z [ 246.933249] "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
> > > > 2025-01-24T02:15:03.9025896Z [ 246.933802] task:ip state:D stack:0 pid:1055 tgid:1055 ppid:1054 flags:0x00004002
> > > > 2025-01-24T02:15:03.9028228Z [ 246.934564] Call Trace:
> > > > 2025-01-24T02:15:03.9029758Z [ 246.934764] <TASK>
> > > > 2025-01-24T02:15:03.9032572Z [ 246.934937] __schedule+0xa91/0xe80
> > > > 2025-01-24T02:15:03.9035126Z [ 246.935224] schedule+0x41/0xb0
> > > > 2025-01-24T02:15:03.9037992Z [ 246.935459] v9fs_evict_inode+0xfe/0x170
> > > > 2025-01-24T02:15:03.9041469Z [ 246.935748] ? __pfx_var_wake_function+0x10/0x10
> > > > 2025-01-24T02:15:03.9043837Z [ 246.936101] evict+0x1ef/0x360
> > > > 2025-01-24T02:15:03.9046624Z [ 246.936340] __dentry_kill+0xb0/0x220
> > > > 2025-01-24T02:15:03.9048855Z [ 246.936610] ? dput+0x3a/0x1d0
> > > > 2025-01-24T02:15:03.9051128Z [ 246.936838] dput+0x114/0x1d0
> > > > 2025-01-24T02:15:03.9053548Z [ 246.937069] __fput+0x136/0x2b0
> > > > 2025-01-24T02:15:03.9056154Z [ 246.937305] task_work_run+0x89/0xc0
> > > > 2025-01-24T02:15:03.9058593Z [ 246.937571] do_exit+0x2c6/0x9c0
> > > > 2025-01-24T02:15:03.9061349Z [ 246.937816] do_group_exit+0xa4/0xb0
> > > > 2025-01-24T02:15:03.9064401Z [ 246.938090] __x64_sys_exit_group+0x17/0x20
> > > > 2025-01-24T02:15:03.9067235Z [ 246.938390] x64_sys_call+0x21a0/0x21a0
> > > > 2025-01-24T02:15:03.9069924Z [ 246.938672] do_syscall_64+0x79/0x120
> > > > 2025-01-24T02:15:03.9072746Z [ 246.938941] ? clear_bhb_loop+0x25/0x80
> > > > 2025-01-24T02:15:03.9075581Z [ 246.939230] ? clear_bhb_loop+0x25/0x80
> > > > 2025-01-24T02:15:03.9079275Z [ 246.939510] entry_SYSCALL_64_after_hwframe+0x76/0x7e
> > > > 2025-01-24T02:15:03.9081976Z [ 246.939875] RIP: 0033:0x7fb86f66f21d
> > > > 2025-01-24T02:15:03.9087533Z [ 246.940153] RSP: 002b:00007ffdb3cf93f8 EFLAGS: 00000202 ORIG_RAX: 00000000000000e7
> > > > 2025-01-24T02:15:03.9092590Z [ 246.940689] RAX: ffffffffffffffda RBX: 00007fb86f785fa8 RCX: 00007fb86f66f21d
> > > > 2025-01-24T02:15:03.9097722Z [ 246.941201] RDX: 00000000000000e7 RSI: ffffffffffffff80 RDI: 0000000000000000
> > > > 2025-01-24T02:15:03.9102762Z [ 246.941705] RBP: 00007ffdb3cf9450 R08: 00007ffdb3cf93a0 R09: 0000000000000000
> > > > 2025-01-24T02:15:03.9107940Z [ 246.942215] R10: 00007ffdb3cf92ff R11: 0000000000000202 R12: 0000000000000001
> > > > 2025-01-24T02:15:03.9113002Z [ 246.942723] R13: 0000000000000000 R14: 0000000000000000 R15: 00007fb86f785fc0
> > > > 2025-01-24T02:15:03.9114614Z [ 246.943244] </TASK>
> > > 
> > > That looks very similar to something I saw in afs testing, with a
> > > similar stack but in afs_evict_inode where it hung waiting in
> > > netfs_wait_for_outstanding_io.
> > > 
> > > David pointed to this bit where there's a double get in
> > > netfs_retry_read_subrequests, since netfs_reissue_read already takes
> > > care of getting a ref on the subrequest:
> > > 
> > > diff --git a/fs/netfs/read_retry.c b/fs/netfs/read_retry.c
> > > index 2290af0d51ac..53d62e31a4cc 100644
> > > --- a/fs/netfs/read_retry.c
> > > +++ b/fs/netfs/read_retry.c
> > > @@ -152,7 +152,6 @@ static void netfs_retry_read_subrequests(struct
> > > netfs_io_request *rreq)
> > > __clear_bit(NETFS_SREQ_BOUNDARY,
> > > &subreq->flags);
> > > 
> > > }
> > > 
> > > - netfs_get_subrequest(subreq,
> > > netfs_sreq_trace_get_resubmit);
> > > netfs_reissue_read(rreq, subreq);
> > > if (subreq == to)
> > > break;
> > > 
> > > That seems to help for my afs test case, I suspect it might help in
> > > your case as well.
> > 
> > Hi Marc. Thank you for the suggestion.
> > 
> > I've just tried this diff on top of bpf-next (d0d106a2bd21):
> > 
> > diff --git a/fs/netfs/read_retry.c b/fs/netfs/read_retry.c
> > index 2290af0d51ac..53d62e31a4cc 100644
> > --- a/fs/netfs/read_retry.c
> > +++ b/fs/netfs/read_retry.c
> > @@ -152,7 +152,6 @@ static void netfs_retry_read_subrequests(struct netfs_io_request *rreq)
> > __clear_bit(NETFS_SREQ_BOUNDARY, &subreq->flags);
> > }
> > 
> > - netfs_get_subrequest(subreq, netfs_sreq_trace_get_resubmit);
> > netfs_reissue_read(rreq, subreq);
> > if (subreq == to)
> > break;
> > 
> > and I'm getting a hung task with the same stack
> > 
> > [ 184.362292] INFO: task modprobe:2527 blocked for more than 20 seconds.
> > [ 184.363173] Tainted: G OE 6.13.0-gd0d106a2bd21-dirty #1
> > [ 184.363651] "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
> > [ 184.364142] task:modprobe state:D stack:0 pid:2527 tgid:2527 ppid:2134 flags:0x00000002
> > [ 184.364743] Call Trace:
> > [ 184.364907] <TASK>
> > [ 184.365057] __schedule+0xa91/0xe80
> > [ 184.365311] schedule+0x41/0xb0
> > [ 184.365525] v9fs_evict_inode+0xfe/0x170
> > [ 184.365782] ? __pfx_var_wake_function+0x10/0x10
> > [ 184.366082] evict+0x1ef/0x360
> > [ 184.366312] __dentry_kill+0xb0/0x220
> > [ 184.366561] ? dput+0x3a/0x1d0
> > [ 184.366765] dput+0x114/0x1d0
> > [ 184.366962] __fput+0x136/0x2b0
> > [ 184.367172] __x64_sys_close+0x9e/0xd0
> > [ 184.367443] do_syscall_64+0x79/0x120
> > [ 184.367685] entry_SYSCALL_64_after_hwframe+0x76/0x7e
> > [ 184.368005] RIP: 0033:0x7f4c6fc7f60b
> > [ 184.368249] RSP: 002b:00007ffc7582beb8 EFLAGS: 00000297 ORIG_RAX: 0000000000000003
> > [ 184.368733] RAX: ffffffffffffffda RBX: 0000555e18cff7a0 RCX: 00007f4c6fc7f60b
> > [ 184.369176] RDX: 00007f4c6fd64ee0 RSI: 0000000000000001 RDI: 0000000000000000
> > [ 184.369634] RBP: 00007ffc7582bee0 R08: 0000000000000000 R09: 0000000000000007
> > [ 184.370078] R10: 0000555e18cff980 R11: 0000000000000297 R12: 0000000000000000
> > [ 184.370544] R13: 00007f4c6fd65030 R14: 0000555e18cff980 R15: 0000555e18d7b750
> > [ 184.371004] </TASK>
> > [ 184.371151]
> > [ 184.371151] Showing all locks held in the system:
> > [ 184.371560] 1 lock held by khungtaskd/32:
> > [ 184.371816] #0: ffffffff83195d90 (rcu_read_lock){....}-{1:3}, at: debug_show_all_locks+0x2e/0x180
> > [ 184.372397] 2 locks held by kworker/u8:21/2134:
> > [ 184.372695] #0: ffff9a5300104d48 ((wq_completion)events_unbound){+.+.}-{0:0}, at: process_scheduled_works+0x23a/0x600
> > [ 184.373376] #1: ffff9e9882187e20 ((work_completion)(&sub_info->work)){+.+.}-{0:0}, at: process_scheduled_works+0x25a/0x600
> > [ 184.374075]
> > [ 184.374182] =============================================
> > 
> > So this appears to be something different.
> > 
> > > Marc
> 
> 
> Looks like there may be a similar issue with the
> netfs_get_subrequest() at line 196, which also precedes a call to
> netfs_reissue_read. Might be worth trying with that removed as well.

Just did that. Got lucky locally, but no changes on CI:
https://github.com/kernel-patches/vmtest/actions/runs/12956261831/job/36142811371

Do you know of any pending patches addressing the double-get issue?
Does this diff reliably fix the hang in afs_evict_inode?

Thanks.

diff --git a/fs/netfs/read_retry.c b/fs/netfs/read_retry.c
index 2290af0d51ac..6c8327f4227c 100644
--- a/fs/netfs/read_retry.c
+++ b/fs/netfs/read_retry.c
@@ -152,7 +152,6 @@ static void netfs_retry_read_subrequests(struct netfs_io_request *rreq)
                                __clear_bit(NETFS_SREQ_BOUNDARY, &subreq->flags);
                        }
 
-                       netfs_get_subrequest(subreq, netfs_sreq_trace_get_resubmit);
                        netfs_reissue_read(rreq, subreq);
                        if (subreq == to)
                                break;
@@ -194,7 +193,6 @@ static void netfs_retry_read_subrequests(struct netfs_io_request *rreq)
                        trace_netfs_sreq_ref(rreq->debug_id, subreq->debug_index,
                                             refcount_read(&subreq->ref),
                                             netfs_sreq_trace_new);
-                       netfs_get_subrequest(subreq, netfs_sreq_trace_get_resubmit);
 
                        list_add(&subreq->rreq_link, &to->rreq_link);
                        to = list_next_entry(to, rreq_link);

> 
> Marc


From stanislawa.laskowska at optiora.pl  Tue Jan 28 00:50:43 2025
From: stanislawa.laskowska at optiora.pl (=?UTF-8?Q? Stanis=C5=82awa_Laskowska ?=)
Date: Tue, 28 Jan 2025 08:50:43 GMT
Subject: Kandydat do pracy
Message-ID: <20250128084501-0.1.7h.nidj.0.2pmbc2rn63@optiora.pl>

Dzie? dobry,

delegujemy sprawdzone osoby ze Wschodu do pracy w firmach produkcyjnych do prostych prac przy produkcji, na magazynie, przy pakowaniu towaru czy kontroli jako?ci. 

Nasza firma organizuje wszelkie formalno?ci oraz zakwaterowanie, co pozwala zmniejszy? koszty po Pa?stwa stronie.

Czy mog? przedstawi? szczeg??y?


Pozdrawiam
Stanis?awa Laskowska


From marek.kucharski at cybercitadel.pl  Thu Jan 30 00:40:22 2025
From: marek.kucharski at cybercitadel.pl (Marek Kucharski)
Date: Thu, 30 Jan 2025 08:40:22 GMT
Subject: =?UTF-8?Q?Prosz=C4=99_o_kontakt?=
Message-ID: <20250130084500-0.1.d8.2daor.0.er3kkqv33f@CyberCitadel.pl>

Dzie? dobry,

Czy jest mo?liwo?? nawi?zania wsp??pracy z Pa?stwem?

Z ch?ci? porozmawiam z osob? zajmuj?c? si? dzia?aniami zwi?zanymi ze sprzeda??.

Pomagamy skutecznie pozyskiwa? nowych klient?w.

Zapraszam do kontaktu.


Pozdrawiam
Marek Kucharski


From gregkh at linuxfoundation.org  Thu Jan 30 06:00:07 2025
From: gregkh at linuxfoundation.org (Greg Kroah-Hartman)
Date: Thu, 30 Jan 2025 15:00:07 +0100
Subject: [PATCH 5.10 018/133] afs: Fix the maximum cell name length
In-Reply-To: <20250130140142.491490528@linuxfoundation.org>
References: <20250130140142.491490528@linuxfoundation.org>
Message-ID: <20250130140143.240058501@linuxfoundation.org>

5.10-stable review patch.  If anyone has any objections, please let me know.

------------------

From: David Howells <dhowells at redhat.com>

[ Upstream commit 8fd56ad6e7c90ac2bddb0741c6b248c8c5d56ac8 ]

The kafs filesystem limits the maximum length of a cell to 256 bytes, but a
problem occurs if someone actually does that: kafs tries to create a
directory under /proc/net/afs/ with the name of the cell, but that fails
with a warning:

        WARNING: CPU: 0 PID: 9 at fs/proc/generic.c:405

because procfs limits the maximum filename length to 255.

However, the DNS limits the maximum lookup length and, by extension, the
maximum cell name, to 255 less two (length count and trailing NUL).

Fix this by limiting the maximum acceptable cellname length to 253.  This
also allows us to be sure we can create the "/afs/.<cell>/" mountpoint too.

Further, split the YFS VL record cell name maximum to be the 256 allowed by
the protocol and ignore the record retrieved by YFSVL.GetCellName if it
exceeds 253.

Fixes: c3e9f888263b ("afs: Implement client support for the YFSVL.GetCellName RPC op")
Reported-by: syzbot+7848fee1f1e5c53f912b at syzkaller.appspotmail.com
Closes: https://lore.kernel.org/r/6776d25d.050a0220.3a8527.0048.GAE at google.com/
Signed-off-by: David Howells <dhowells at redhat.com>
Link: https://lore.kernel.org/r/376236.1736180460 at warthog.procyon.org.uk
Tested-by: syzbot+7848fee1f1e5c53f912b at syzkaller.appspotmail.com
cc: Marc Dionne <marc.dionne at auristor.com>
cc: linux-afs at lists.infradead.org
Signed-off-by: Christian Brauner <brauner at kernel.org>
Signed-off-by: Sasha Levin <sashal at kernel.org>
---
 fs/afs/afs.h      | 2 +-
 fs/afs/afs_vl.h   | 1 +
 fs/afs/vl_alias.c | 8 ++++++--
 fs/afs/vlclient.c | 2 +-
 4 files changed, 9 insertions(+), 4 deletions(-)

diff --git a/fs/afs/afs.h b/fs/afs/afs.h
index 432cb4b23961..3ea5f3e3c922 100644
--- a/fs/afs/afs.h
+++ b/fs/afs/afs.h
@@ -10,7 +10,7 @@
 
 #include <linux/in.h>
 
-#define AFS_MAXCELLNAME		256  	/* Maximum length of a cell name */
+#define AFS_MAXCELLNAME		253  	/* Maximum length of a cell name (DNS limited) */
 #define AFS_MAXVOLNAME		64  	/* Maximum length of a volume name */
 #define AFS_MAXNSERVERS		8   	/* Maximum servers in a basic volume record */
 #define AFS_NMAXNSERVERS	13  	/* Maximum servers in a N/U-class volume record */
diff --git a/fs/afs/afs_vl.h b/fs/afs/afs_vl.h
index 9c65ffb8a523..8da0899fbc08 100644
--- a/fs/afs/afs_vl.h
+++ b/fs/afs/afs_vl.h
@@ -13,6 +13,7 @@
 #define AFS_VL_PORT		7003	/* volume location service port */
 #define VL_SERVICE		52	/* RxRPC service ID for the Volume Location service */
 #define YFS_VL_SERVICE		2503	/* Service ID for AuriStor upgraded VL service */
+#define YFS_VL_MAXCELLNAME	256  	/* Maximum length of a cell name in YFS protocol */
 
 enum AFSVL_Operations {
 	VLGETENTRYBYID		= 503,	/* AFS Get VLDB entry by ID */
diff --git a/fs/afs/vl_alias.c b/fs/afs/vl_alias.c
index f04a80e4f5c3..83cf1bfbe343 100644
--- a/fs/afs/vl_alias.c
+++ b/fs/afs/vl_alias.c
@@ -302,6 +302,7 @@ static char *afs_vl_get_cell_name(struct afs_cell *cell, struct key *key)
 static int yfs_check_canonical_cell_name(struct afs_cell *cell, struct key *key)
 {
 	struct afs_cell *master;
+	size_t name_len;
 	char *cell_name;
 
 	cell_name = afs_vl_get_cell_name(cell, key);
@@ -313,8 +314,11 @@ static int yfs_check_canonical_cell_name(struct afs_cell *cell, struct key *key)
 		return 0;
 	}
 
-	master = afs_lookup_cell(cell->net, cell_name, strlen(cell_name),
-				 NULL, false);
+	name_len = strlen(cell_name);
+	if (!name_len || name_len > AFS_MAXCELLNAME)
+		master = ERR_PTR(-EOPNOTSUPP);
+	else
+		master = afs_lookup_cell(cell->net, cell_name, name_len, NULL, false);
 	kfree(cell_name);
 	if (IS_ERR(master))
 		return PTR_ERR(master);
diff --git a/fs/afs/vlclient.c b/fs/afs/vlclient.c
index dc9327332f06..882f0727c3cd 100644
--- a/fs/afs/vlclient.c
+++ b/fs/afs/vlclient.c
@@ -670,7 +670,7 @@ static int afs_deliver_yfsvl_get_cell_name(struct afs_call *call)
 			return ret;
 
 		namesz = ntohl(call->tmp);
-		if (namesz > AFS_MAXCELLNAME)
+		if (namesz > YFS_VL_MAXCELLNAME)
 			return afs_protocol_error(call, afs_eproto_cellname_len);
 		paddedsz = (namesz + 3) & ~3;
 		call->count = namesz;
-- 
2.39.5





From gorcunov at gmail.com  Thu Jan 30 13:39:43 2025
From: gorcunov at gmail.com (Cyrill Gorcunov)
Date: Fri, 31 Jan 2025 00:39:43 +0300
Subject: [PATCH] afs: Add missing init for srx_service member
Message-ID: <Z5vxn699b0YtTAc-@grain>

Both afs_merge_fs_addr4 and afs_merge_fs_addr6 helpers allocate
sockaddr_rxrpc structure on kernel stack and never init srx_service
member before passing it down for peer lookup/creation. Looks
insignificant in current context but better be on a safe side for
future use. Thus initialize them explicitly.

Signed-off-by: Cyrill Gorcunov <gorcunov at gmail.com>
Cc: David Howells <dhowells at redhat.com>
Cc: Marc Dionne <marc.dionne at auristor.com>
---
 fs/afs/addr_list.c |    2 ++
 1 file changed, 2 insertions(+)

Index: linux-tip.git/fs/afs/addr_list.c
===================================================================
--- linux-tip.git.orig/fs/afs/addr_list.c
+++ linux-tip.git/fs/afs/addr_list.c
@@ -291,6 +291,7 @@ int afs_merge_fs_addr4(struct afs_net *n
 		return 0;
 
 	srx.srx_family = AF_RXRPC;
+	srx.srx_service = 0;
 	srx.transport_type = SOCK_DGRAM;
 	srx.transport_len = sizeof(srx.transport.sin);
 	srx.transport.sin.sin_family = AF_INET;
@@ -335,6 +336,7 @@ int afs_merge_fs_addr6(struct afs_net *n
 		return 0;
 
 	srx.srx_family = AF_RXRPC;
+	srx.srx_service = 0;
 	srx.transport_type = SOCK_DGRAM;
 	srx.transport_len = sizeof(srx.transport.sin6);
 	srx.transport.sin6.sin6_family = AF_INET6;


From patchwork-bot+netdevbpf at kernel.org  Mon Jan 27 15:10:41 2025
From: patchwork-bot+netdevbpf at kernel.org (patchwork-bot+netdevbpf at kernel.org)
Date: Mon, 27 Jan 2025 23:10:41 +0000
Subject: [PATCH net] rxrpc, afs: Fix peer hash locking vs RCU callback
In-Reply-To: <2095618.1737622752@warthog.procyon.org.uk>
References: <2095618.1737622752@warthog.procyon.org.uk>
Message-ID: <173801944199.3253418.8964414973990474171.git-patchwork-notify@kernel.org>

Hello:

This patch was applied to netdev/net.git (main)
by Jakub Kicinski <kuba at kernel.org>:

On Thu, 23 Jan 2025 08:59:12 +0000 you wrote:
> In its address list, afs now retains pointers to and refs on one or more
> rxrpc_peer objects.  The address list is freed under RCU and at this time,
> it puts the refs on those peers.
> 
> Now, when an rxrpc_peer object runs out of refs, it gets removed from the
> peer hash table and, for that, rxrpc has to take a spinlock.  However, it
> is now being called from afs's RCU cleanup, which takes place in BH
> context - but it is just taking an ordinary spinlock.
> 
> [...]

Here is the summary with links:
  - [net] rxrpc, afs: Fix peer hash locking vs RCU callback
    https://git.kernel.org/netdev/net/c/79d458c13056

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html




From gregkh at linuxfoundation.org  Thu Jan 30 06:01:24 2025
From: gregkh at linuxfoundation.org (Greg Kroah-Hartman)
Date: Thu, 30 Jan 2025 15:01:24 +0100
Subject: [PATCH 5.4 65/91] kheaders: Ignore silly-rename files
In-Reply-To: <20250130140133.662535583@linuxfoundation.org>
References: <20250130140133.662535583@linuxfoundation.org>
Message-ID: <20250130140136.291051235@linuxfoundation.org>

5.4-stable review patch.  If anyone has any objections, please let me know.

------------------

From: David Howells <dhowells at redhat.com>

[ Upstream commit 973b710b8821c3401ad7a25360c89e94b26884ac ]

Tell tar to ignore silly-rename files (".__afs*" and ".nfs*") when building
the header archive.  These occur when a file that is open is unlinked
locally, but hasn't yet been closed.  Such files are visible to the user
via the getdents() syscall and so programs may want to do things with them.

During the kernel build, such files may be made during the processing of
header files and the cleanup may get deferred by fput() which may result in
tar seeing these files when it reads the directory, but they may have
disappeared by the time it tries to open them, causing tar to fail with an
error.  Further, we don't want to include them in the tarball if they still
exist.

With CONFIG_HEADERS_INSTALL=y, something like the following may be seen:

   find: './kernel/.tmp_cpio_dir/include/dt-bindings/reset/.__afs2080': No such file or directory
   tar: ./include/linux/greybus/.__afs3C95: File removed before we read it

The find warning doesn't seem to cause a problem.

Fix this by telling tar when called from in gen_kheaders.sh to exclude such
files.  This only affects afs and nfs; cifs uses the Windows Hidden
attribute to prevent the file from being seen.

Signed-off-by: David Howells <dhowells at redhat.com>
Link: https://lore.kernel.org/r/20241213135013.2964079-2-dhowells at redhat.com
cc: Masahiro Yamada <masahiroy at kernel.org>
cc: Marc Dionne <marc.dionne at auristor.com>
cc: linux-afs at lists.infradead.org
cc: linux-nfs at vger.kernel.org
cc: linux-kernel at vger.kernel.org
Signed-off-by: Christian Brauner <brauner at kernel.org>
Signed-off-by: Sasha Levin <sashal at kernel.org>
---
 kernel/gen_kheaders.sh | 1 +
 1 file changed, 1 insertion(+)

diff --git a/kernel/gen_kheaders.sh b/kernel/gen_kheaders.sh
index 206ab3d41ee76..7fc44d8da2052 100755
--- a/kernel/gen_kheaders.sh
+++ b/kernel/gen_kheaders.sh
@@ -84,6 +84,7 @@ find $cpio_dir -type f -print0 |
 
 # Create archive and try to normalize metadata for reproducibility.
 tar "${KBUILD_BUILD_TIMESTAMP:+--mtime=$KBUILD_BUILD_TIMESTAMP}" \
+    --exclude=".__afs*" --exclude=".nfs*" \
     --owner=0 --group=0 --sort=name --numeric-owner --mode=u=rw,go=r,a+X \
     -I $XZ -cf $tarfile -C $cpio_dir/ . > /dev/null
 
-- 
2.39.5





From gregkh at linuxfoundation.org  Thu Jan 30 06:01:17 2025
From: gregkh at linuxfoundation.org (Greg Kroah-Hartman)
Date: Thu, 30 Jan 2025 15:01:17 +0100
Subject: [PATCH 5.10 088/133] kheaders: Ignore silly-rename files
In-Reply-To: <20250130140142.491490528@linuxfoundation.org>
References: <20250130140142.491490528@linuxfoundation.org>
Message-ID: <20250130140146.063287842@linuxfoundation.org>

5.10-stable review patch.  If anyone has any objections, please let me know.

------------------

From: David Howells <dhowells at redhat.com>

[ Upstream commit 973b710b8821c3401ad7a25360c89e94b26884ac ]

Tell tar to ignore silly-rename files (".__afs*" and ".nfs*") when building
the header archive.  These occur when a file that is open is unlinked
locally, but hasn't yet been closed.  Such files are visible to the user
via the getdents() syscall and so programs may want to do things with them.

During the kernel build, such files may be made during the processing of
header files and the cleanup may get deferred by fput() which may result in
tar seeing these files when it reads the directory, but they may have
disappeared by the time it tries to open them, causing tar to fail with an
error.  Further, we don't want to include them in the tarball if they still
exist.

With CONFIG_HEADERS_INSTALL=y, something like the following may be seen:

   find: './kernel/.tmp_cpio_dir/include/dt-bindings/reset/.__afs2080': No such file or directory
   tar: ./include/linux/greybus/.__afs3C95: File removed before we read it

The find warning doesn't seem to cause a problem.

Fix this by telling tar when called from in gen_kheaders.sh to exclude such
files.  This only affects afs and nfs; cifs uses the Windows Hidden
attribute to prevent the file from being seen.

Signed-off-by: David Howells <dhowells at redhat.com>
Link: https://lore.kernel.org/r/20241213135013.2964079-2-dhowells at redhat.com
cc: Masahiro Yamada <masahiroy at kernel.org>
cc: Marc Dionne <marc.dionne at auristor.com>
cc: linux-afs at lists.infradead.org
cc: linux-nfs at vger.kernel.org
cc: linux-kernel at vger.kernel.org
Signed-off-by: Christian Brauner <brauner at kernel.org>
Signed-off-by: Sasha Levin <sashal at kernel.org>
---
 kernel/gen_kheaders.sh | 1 +
 1 file changed, 1 insertion(+)

diff --git a/kernel/gen_kheaders.sh b/kernel/gen_kheaders.sh
index 206ab3d41ee76..7fc44d8da2052 100755
--- a/kernel/gen_kheaders.sh
+++ b/kernel/gen_kheaders.sh
@@ -84,6 +84,7 @@ find $cpio_dir -type f -print0 |
 
 # Create archive and try to normalize metadata for reproducibility.
 tar "${KBUILD_BUILD_TIMESTAMP:+--mtime=$KBUILD_BUILD_TIMESTAMP}" \
+    --exclude=".__afs*" --exclude=".nfs*" \
     --owner=0 --group=0 --sort=name --numeric-owner --mode=u=rw,go=r,a+X \
     -I $XZ -cf $tarfile -C $cpio_dir/ . > /dev/null
 
-- 
2.39.5





