* [OE-core][scarthgap 07/16] openssl: patch CVE-2025-27587
2025-07-24 21:35 [OE-core][scarthgap 00/16] Patch review Steve Sakoman
` (5 preceding siblings ...)
2025-07-24 21:35 ` [OE-core][scarthgap 06/16] openssl: CVE-2024-41996 Steve Sakoman
@ 2025-07-24 21:35 ` Steve Sakoman
2025-07-24 21:35 ` [OE-core][scarthgap 08/16] libpam: fix CVE-2025-6020 Steve Sakoman
` (8 subsequent siblings)
15 siblings, 0 replies; 19+ messages in thread
From: Steve Sakoman @ 2025-07-24 21:35 UTC (permalink / raw)
To: openembedded-core
From: Peter Marko <peter.marko@siemens.com>
Pick commits for Minerva fix between 3.2.4 and 3.2.5 release.
Update to 3.2.5 is blocked due to problem with python ptest errors, so
use patch instead of upgrade for now.
Signed-off-by: Peter Marko <peter.marko@siemens.com>
Signed-off-by: Steve Sakoman <steve@sakoman.com>
---
.../openssl/openssl/CVE-2025-27587-1.patch | 1918 +++++++++++++++++
.../openssl/openssl/CVE-2025-27587-2.patch | 129 ++
.../openssl/openssl_3.2.4.bb | 2 +
3 files changed, 2049 insertions(+)
create mode 100644 meta/recipes-connectivity/openssl/openssl/CVE-2025-27587-1.patch
create mode 100644 meta/recipes-connectivity/openssl/openssl/CVE-2025-27587-2.patch
diff --git a/meta/recipes-connectivity/openssl/openssl/CVE-2025-27587-1.patch b/meta/recipes-connectivity/openssl/openssl/CVE-2025-27587-1.patch
new file mode 100644
index 0000000000..eb3fc52dca
--- /dev/null
+++ b/meta/recipes-connectivity/openssl/openssl/CVE-2025-27587-1.patch
@@ -0,0 +1,1918 @@
+From 14ac0f0e4e1f36793d09b41ffd5e482575289ab2 Mon Sep 17 00:00:00 2001
+From: Danny Tsen <dtsen@us.ibm.com>
+Date: Tue, 11 Feb 2025 13:48:01 -0500
+Subject: [PATCH] Fix Minerva timing side-channel signal for P-384 curve on PPC
+
+1. bn_ppc.c: Used bn_mul_mont_int() instead of bn_mul_mont_300_fixed_n6()
+ for Montgomery multiplication.
+2. ecp_nistp384-ppc64.pl:
+ - Re-wrote p384_felem_mul and p384_felem_square for easier maintenance with
+ minumum perl wrapper.
+ - Implemented p384_felem_reduce, p384_felem_mul_reduce and p384_felem_square_reduce.
+ - Implemented p384_felem_diff64, felem_diff_128_64 and felem_diff128 in assembly.
+3. ecp_nistp384.c:
+ - Added wrapper function for p384_felem_mul_reduce and p384_felem_square_reduce.
+
+Signed-off-by: Danny Tsen <dtsen@us.ibm.com>
+
+Reviewed-by: Dmitry Belyavskiy <beldmit@gmail.com>
+Reviewed-by: Tomas Mraz <tomas@openssl.org>
+(Merged from https://github.com/openssl/openssl/pull/26709)
+
+(cherry picked from commit 85cabd94958303859b1551364a609d4ff40b67a5)
+
+CVE: CVE-2025-27587
+Upstream-Status: Backport [https://github.com/openssl/openssl/commit/14ac0f0e4e1f36793d09b41ffd5e482575289ab2]
+Signed-off-by: Peter Marko <peter.marko@siemens.com>
+---
+ crypto/bn/bn_ppc.c | 3 +
+ crypto/ec/asm/ecp_nistp384-ppc64.pl | 1724 +++++++++++++++++++++++----
+ crypto/ec/ecp_nistp384.c | 28 +-
+ 3 files changed, 1504 insertions(+), 251 deletions(-)
+
+diff --git a/crypto/bn/bn_ppc.c b/crypto/bn/bn_ppc.c
+index 1e9421bee2..29293bad55 100644
+--- a/crypto/bn/bn_ppc.c
++++ b/crypto/bn/bn_ppc.c
+@@ -41,12 +41,15 @@ int bn_mul_mont(BN_ULONG *rp, const BN_ULONG *ap, const BN_ULONG *bp,
+ */
+
+ #if defined(_ARCH_PPC64) && !defined(__ILP32__)
++ /* Minerva side-channel fix danny */
++# if defined(USE_FIXED_N6)
+ if (num == 6) {
+ if (OPENSSL_ppccap_P & PPC_MADD300)
+ return bn_mul_mont_300_fixed_n6(rp, ap, bp, np, n0, num);
+ else
+ return bn_mul_mont_fixed_n6(rp, ap, bp, np, n0, num);
+ }
++# endif
+ #endif
+
+ return bn_mul_mont_int(rp, ap, bp, np, n0, num);
+diff --git a/crypto/ec/asm/ecp_nistp384-ppc64.pl b/crypto/ec/asm/ecp_nistp384-ppc64.pl
+index 28f4168e52..b663bddfc6 100755
+--- a/crypto/ec/asm/ecp_nistp384-ppc64.pl
++++ b/crypto/ec/asm/ecp_nistp384-ppc64.pl
+@@ -7,13 +7,15 @@
+ # https://www.openssl.org/source/license.html
+ #
+ # ====================================================================
+-# Written by Rohan McLure <rmclure@linux.ibm.com> for the OpenSSL
+-# project.
++# Written by Danny Tsen <dtsen@us.ibm.com> # for the OpenSSL project.
++#
++# Copyright 2025- IBM Corp.
+ # ====================================================================
+ #
+-# p384 lower-level primitives for PPC64 using vector instructions.
++# p384 lower-level primitives for PPC64.
+ #
+
++
+ use strict;
+ use warnings;
+
+@@ -21,7 +23,7 @@ my $flavour = shift;
+ my $output = "";
+ while (($output=shift) && ($output!~/\w[\w\-]*\.\w+$/)) {}
+ if (!$output) {
+- $output = "-";
++ $output = "-";
+ }
+
+ my ($xlate, $dir);
+@@ -35,271 +37,1495 @@ open OUT,"| \"$^X\" $xlate $flavour $output";
+
+ my $code = "";
+
+-my ($sp, $outp, $savelr, $savesp) = ("r1", "r3", "r10", "r12");
+-
+-my $vzero = "v32";
+-
+-sub startproc($)
+-{
+- my ($name) = @_;
+-
+- $code.=<<___;
+- .globl ${name}
+- .align 5
+-${name}:
+-
+-___
+-}
+-
+-sub endproc($)
+-{
+- my ($name) = @_;
+-
+- $code.=<<___;
+- blr
+- .size ${name},.-${name}
+-
+-___
+-}
+-
+-sub load_vrs($$)
+-{
+- my ($pointer, $reg_list) = @_;
+-
+- for (my $i = 0; $i <= 6; $i++) {
+- my $offset = $i * 8;
+- $code.=<<___;
+- lxsd $reg_list->[$i],$offset($pointer)
+-___
+- }
+-
+- $code.=<<___;
+-
+-___
+-}
+-
+-sub store_vrs($$)
+-{
+- my ($pointer, $reg_list) = @_;
+-
+- for (my $i = 0; $i <= 12; $i++) {
+- my $offset = $i * 16;
+- $code.=<<___;
+- stxv $reg_list->[$i],$offset($pointer)
+-___
+- }
+-
+- $code.=<<___;
+-
+-___
+-}
+-
+ $code.=<<___;
+-.machine "any"
++.machine "any"
+ .text
+
+-___
++.globl p384_felem_mul
++.type p384_felem_mul,\@function
++.align 4
++p384_felem_mul:
+
+-{
+- # mul/square common
+- my ($t1, $t2, $t3, $t4) = ("v33", "v34", "v42", "v43");
+- my ($zero, $one) = ("r8", "r9");
+- my $out = "v51";
++ stdu 1, -176(1)
++ mflr 0
++ std 14, 56(1)
++ std 15, 64(1)
++ std 16, 72(1)
++ std 17, 80(1)
++ std 18, 88(1)
++ std 19, 96(1)
++ std 20, 104(1)
++ std 21, 112(1)
++ std 22, 120(1)
+
+- {
+- #
+- # p384_felem_mul
+- #
++ bl _p384_felem_mul_core
+
+- my ($in1p, $in2p) = ("r4", "r5");
+- my @in1 = map("v$_",(44..50));
+- my @in2 = map("v$_",(35..41));
++ mtlr 0
++ ld 14, 56(1)
++ ld 15, 64(1)
++ ld 16, 72(1)
++ ld 17, 80(1)
++ ld 18, 88(1)
++ ld 19, 96(1)
++ ld 20, 104(1)
++ ld 21, 112(1)
++ ld 22, 120(1)
++ addi 1, 1, 176
++ blr
++.size p384_felem_mul,.-p384_felem_mul
+
+- startproc("p384_felem_mul");
++.globl p384_felem_square
++.type p384_felem_square,\@function
++.align 4
++p384_felem_square:
+
+- $code.=<<___;
+- vspltisw $vzero,0
++ stdu 1, -176(1)
++ mflr 0
++ std 14, 56(1)
++ std 15, 64(1)
++ std 16, 72(1)
++ std 17, 80(1)
+
+-___
++ bl _p384_felem_square_core
+
+- load_vrs($in1p, \@in1);
+- load_vrs($in2p, \@in2);
+-
+- $code.=<<___;
+- vmsumudm $out,$in1[0],$in2[0],$vzero
+- stxv $out,0($outp)
+-
+- xxpermdi $t1,$in1[0],$in1[1],0b00
+- xxpermdi $t2,$in2[1],$in2[0],0b00
+- vmsumudm $out,$t1,$t2,$vzero
+- stxv $out,16($outp)
+-
+- xxpermdi $t2,$in2[2],$in2[1],0b00
+- vmsumudm $out,$t1,$t2,$vzero
+- vmsumudm $out,$in1[2],$in2[0],$out
+- stxv $out,32($outp)
+-
+- xxpermdi $t2,$in2[1],$in2[0],0b00
+- xxpermdi $t3,$in1[2],$in1[3],0b00
+- xxpermdi $t4,$in2[3],$in2[2],0b00
+- vmsumudm $out,$t1,$t4,$vzero
+- vmsumudm $out,$t3,$t2,$out
+- stxv $out,48($outp)
+-
+- xxpermdi $t2,$in2[4],$in2[3],0b00
+- xxpermdi $t4,$in2[2],$in2[1],0b00
+- vmsumudm $out,$t1,$t2,$vzero
+- vmsumudm $out,$t3,$t4,$out
+- vmsumudm $out,$in1[4],$in2[0],$out
+- stxv $out,64($outp)
+-
+- xxpermdi $t2,$in2[5],$in2[4],0b00
+- xxpermdi $t4,$in2[3],$in2[2],0b00
+- vmsumudm $out,$t1,$t2,$vzero
+- vmsumudm $out,$t3,$t4,$out
+- xxpermdi $t4,$in2[1],$in2[0],0b00
+- xxpermdi $t1,$in1[4],$in1[5],0b00
+- vmsumudm $out,$t1,$t4,$out
+- stxv $out,80($outp)
+-
+- xxpermdi $t1,$in1[0],$in1[1],0b00
+- xxpermdi $t2,$in2[6],$in2[5],0b00
+- xxpermdi $t4,$in2[4],$in2[3],0b00
+- vmsumudm $out,$t1,$t2,$vzero
+- vmsumudm $out,$t3,$t4,$out
+- xxpermdi $t2,$in2[2],$in2[1],0b00
+- xxpermdi $t1,$in1[4],$in1[5],0b00
+- vmsumudm $out,$t1,$t2,$out
+- vmsumudm $out,$in1[6],$in2[0],$out
+- stxv $out,96($outp)
+-
+- xxpermdi $t1,$in1[1],$in1[2],0b00
+- xxpermdi $t2,$in2[6],$in2[5],0b00
+- xxpermdi $t3,$in1[3],$in1[4],0b00
+- vmsumudm $out,$t1,$t2,$vzero
+- vmsumudm $out,$t3,$t4,$out
+- xxpermdi $t3,$in2[2],$in2[1],0b00
+- xxpermdi $t1,$in1[5],$in1[6],0b00
+- vmsumudm $out,$t1,$t3,$out
+- stxv $out,112($outp)
+-
+- xxpermdi $t1,$in1[2],$in1[3],0b00
+- xxpermdi $t3,$in1[4],$in1[5],0b00
+- vmsumudm $out,$t1,$t2,$vzero
+- vmsumudm $out,$t3,$t4,$out
+- vmsumudm $out,$in1[6],$in2[2],$out
+- stxv $out,128($outp)
+-
+- xxpermdi $t1,$in1[3],$in1[4],0b00
+- vmsumudm $out,$t1,$t2,$vzero
+- xxpermdi $t1,$in1[5],$in1[6],0b00
+- vmsumudm $out,$t1,$t4,$out
+- stxv $out,144($outp)
+-
+- vmsumudm $out,$t3,$t2,$vzero
+- vmsumudm $out,$in1[6],$in2[4],$out
+- stxv $out,160($outp)
+-
+- vmsumudm $out,$t1,$t2,$vzero
+- stxv $out,176($outp)
+-
+- vmsumudm $out,$in1[6],$in2[6],$vzero
+- stxv $out,192($outp)
+-___
++ mtlr 0
++ ld 14, 56(1)
++ ld 15, 64(1)
++ ld 16, 72(1)
++ ld 17, 80(1)
++ addi 1, 1, 176
++ blr
++.size p384_felem_square,.-p384_felem_square
+
+- endproc("p384_felem_mul");
+- }
++#
++# Felem mul core function -
++# r3, r4 and r5 need to pre-loaded.
++#
++.type _p384_felem_mul_core,\@function
++.align 4
++_p384_felem_mul_core:
+
+- {
+- #
+- # p384_felem_square
+- #
++ ld 6,0(4)
++ ld 14,0(5)
++ ld 7,8(4)
++ ld 15,8(5)
++ ld 8,16(4)
++ ld 16,16(5)
++ ld 9,24(4)
++ ld 17,24(5)
++ ld 10,32(4)
++ ld 18,32(5)
++ ld 11,40(4)
++ ld 19,40(5)
++ ld 12,48(4)
++ ld 20,48(5)
+
+- my ($inp) = ("r4");
+- my @in = map("v$_",(44..50));
+- my @inx2 = map("v$_",(35..41));
++ # out0
++ mulld 21, 14, 6
++ mulhdu 22, 14, 6
++ std 21, 0(3)
++ std 22, 8(3)
+
+- startproc("p384_felem_square");
++ vxor 0, 0, 0
+
+- $code.=<<___;
+- vspltisw $vzero,0
++ # out1
++ mtvsrdd 32+13, 14, 6
++ mtvsrdd 32+14, 7, 15
++ vmsumudm 1, 13, 14, 0
+
+-___
++ # out2
++ mtvsrdd 32+15, 15, 6
++ mtvsrdd 32+16, 7, 16
++ mtvsrdd 32+17, 0, 8
++ mtvsrdd 32+18, 0, 14
++ vmsumudm 19, 15, 16, 0
++ vmsumudm 2, 17, 18, 19
+
+- load_vrs($inp, \@in);
++ # out3
++ mtvsrdd 32+13, 16, 6
++ mtvsrdd 32+14, 7, 17
++ mtvsrdd 32+15, 14, 8
++ mtvsrdd 32+16, 9, 15
++ vmsumudm 19, 13, 14, 0
++ vmsumudm 3, 15, 16, 19
+
+- $code.=<<___;
+- li $zero,0
+- li $one,1
+- mtvsrdd $t1,$one,$zero
+-___
++ # out4
++ mtvsrdd 32+13, 17, 6
++ mtvsrdd 32+14, 7, 18
++ mtvsrdd 32+15, 15, 8
++ mtvsrdd 32+16, 9, 16
++ mtvsrdd 32+17, 0, 10
++ mtvsrdd 32+18, 0, 14
++ vmsumudm 19, 13, 14, 0
++ vmsumudm 4, 15, 16, 19
++ vmsumudm 4, 17, 18, 4
+
+- for (my $i = 0; $i <= 6; $i++) {
+- $code.=<<___;
+- vsld $inx2[$i],$in[$i],$t1
+-___
+- }
+-
+- $code.=<<___;
+- vmsumudm $out,$in[0],$in[0],$vzero
+- stxv $out,0($outp)
+-
+- vmsumudm $out,$in[0],$inx2[1],$vzero
+- stxv $out,16($outp)
+-
+- vmsumudm $out,$in[0],$inx2[2],$vzero
+- vmsumudm $out,$in[1],$in[1],$out
+- stxv $out,32($outp)
+-
+- xxpermdi $t1,$in[0],$in[1],0b00
+- xxpermdi $t2,$inx2[3],$inx2[2],0b00
+- vmsumudm $out,$t1,$t2,$vzero
+- stxv $out,48($outp)
+-
+- xxpermdi $t4,$inx2[4],$inx2[3],0b00
+- vmsumudm $out,$t1,$t4,$vzero
+- vmsumudm $out,$in[2],$in[2],$out
+- stxv $out,64($outp)
+-
+- xxpermdi $t2,$inx2[5],$inx2[4],0b00
+- vmsumudm $out,$t1,$t2,$vzero
+- vmsumudm $out,$in[2],$inx2[3],$out
+- stxv $out,80($outp)
+-
+- xxpermdi $t2,$inx2[6],$inx2[5],0b00
+- vmsumudm $out,$t1,$t2,$vzero
+- vmsumudm $out,$in[2],$inx2[4],$out
+- vmsumudm $out,$in[3],$in[3],$out
+- stxv $out,96($outp)
+-
+- xxpermdi $t3,$in[1],$in[2],0b00
+- vmsumudm $out,$t3,$t2,$vzero
+- vmsumudm $out,$in[3],$inx2[4],$out
+- stxv $out,112($outp)
+-
+- xxpermdi $t1,$in[2],$in[3],0b00
+- vmsumudm $out,$t1,$t2,$vzero
+- vmsumudm $out,$in[4],$in[4],$out
+- stxv $out,128($outp)
+-
+- xxpermdi $t1,$in[3],$in[4],0b00
+- vmsumudm $out,$t1,$t2,$vzero
+- stxv $out,144($outp)
+-
+- vmsumudm $out,$in[4],$inx2[6],$vzero
+- vmsumudm $out,$in[5],$in[5],$out
+- stxv $out,160($outp)
+-
+- vmsumudm $out,$in[5],$inx2[6],$vzero
+- stxv $out,176($outp)
+-
+- vmsumudm $out,$in[6],$in[6],$vzero
+- stxv $out,192($outp)
+-___
++ # out5
++ mtvsrdd 32+13, 18, 6
++ mtvsrdd 32+14, 7, 19
++ mtvsrdd 32+15, 16, 8
++ mtvsrdd 32+16, 9, 17
++ mtvsrdd 32+17, 14, 10
++ mtvsrdd 32+18, 11, 15
++ vmsumudm 19, 13, 14, 0
++ vmsumudm 5, 15, 16, 19
++ vmsumudm 5, 17, 18, 5
++
++ stxv 32+1, 16(3)
++ stxv 32+2, 32(3)
++ stxv 32+3, 48(3)
++ stxv 32+4, 64(3)
++ stxv 32+5, 80(3)
++
++ # out6
++ mtvsrdd 32+13, 19, 6
++ mtvsrdd 32+14, 7, 20
++ mtvsrdd 32+15, 17, 8
++ mtvsrdd 32+16, 9, 18
++ mtvsrdd 32+17, 15, 10
++ mtvsrdd 32+18, 11, 16
++ vmsumudm 19, 13, 14, 0
++ vmsumudm 6, 15, 16, 19
++ mtvsrdd 32+13, 0, 12
++ mtvsrdd 32+14, 0, 14
++ vmsumudm 19, 17, 18, 6
++ vmsumudm 6, 13, 14, 19
++
++ # out7
++ mtvsrdd 32+13, 19, 7
++ mtvsrdd 32+14, 8, 20
++ mtvsrdd 32+15, 17, 9
++ mtvsrdd 32+16, 10, 18
++ mtvsrdd 32+17, 15, 11
++ mtvsrdd 32+18, 12, 16
++ vmsumudm 19, 13, 14, 0
++ vmsumudm 7, 15, 16, 19
++ vmsumudm 7, 17, 18, 7
++
++ # out8
++ mtvsrdd 32+13, 19, 8
++ mtvsrdd 32+14, 9, 20
++ mtvsrdd 32+15, 17, 10
++ mtvsrdd 32+16, 11, 18
++ mtvsrdd 32+17, 0, 12
++ mtvsrdd 32+18, 0, 16
++ vmsumudm 19, 13, 14, 0
++ vmsumudm 8, 15, 16, 19
++ vmsumudm 8, 17, 18, 8
++
++ # out9
++ mtvsrdd 32+13, 19, 9
++ mtvsrdd 32+14, 10, 20
++ mtvsrdd 32+15, 17, 11
++ mtvsrdd 32+16, 12, 18
++ vmsumudm 19, 13, 14, 0
++ vmsumudm 9, 15, 16, 19
++
++ # out10
++ mtvsrdd 32+13, 19, 10
++ mtvsrdd 32+14, 11, 20
++ mtvsrdd 32+15, 0, 12
++ mtvsrdd 32+16, 0, 18
++ vmsumudm 19, 13, 14, 0
++ vmsumudm 10, 15, 16, 19
++
++ # out11
++ mtvsrdd 32+17, 19, 11
++ mtvsrdd 32+18, 12, 20
++ vmsumudm 11, 17, 18, 0
++
++ stxv 32+6, 96(3)
++ stxv 32+7, 112(3)
++ stxv 32+8, 128(3)
++ stxv 32+9, 144(3)
++ stxv 32+10, 160(3)
++ stxv 32+11, 176(3)
++
++ # out12
++ mulld 21, 20, 12
++ mulhdu 22, 20, 12 # out12
++
++ std 21, 192(3)
++ std 22, 200(3)
++
++ blr
++.size _p384_felem_mul_core,.-_p384_felem_mul_core
++
++#
++# Felem square core function -
++# r3 and r4 need to pre-loaded.
++#
++.type _p384_felem_square_core,\@function
++.align 4
++_p384_felem_square_core:
++
++ ld 6, 0(4)
++ ld 7, 8(4)
++ ld 8, 16(4)
++ ld 9, 24(4)
++ ld 10, 32(4)
++ ld 11, 40(4)
++ ld 12, 48(4)
++
++ vxor 0, 0, 0
++
++ # out0
++ mulld 14, 6, 6
++ mulhdu 15, 6, 6
++ std 14, 0(3)
++ std 15, 8(3)
++
++ # out1
++ add 14, 6, 6
++ mtvsrdd 32+13, 0, 14
++ mtvsrdd 32+14, 0, 7
++ vmsumudm 1, 13, 14, 0
++
++ # out2
++ mtvsrdd 32+15, 7, 14
++ mtvsrdd 32+16, 7, 8
++ vmsumudm 2, 15, 16, 0
++
++ # out3
++ add 15, 7, 7
++ mtvsrdd 32+13, 8, 14
++ mtvsrdd 32+14, 15, 9
++ vmsumudm 3, 13, 14, 0
++
++ # out4
++ mtvsrdd 32+13, 9, 14
++ mtvsrdd 32+14, 15, 10
++ mtvsrdd 32+15, 0, 8
++ vmsumudm 4, 13, 14, 0
++ vmsumudm 4, 15, 15, 4
++
++ # out5
++ mtvsrdd 32+13, 10, 14
++ mtvsrdd 32+14, 15, 11
++ add 16, 8, 8
++ mtvsrdd 32+15, 0, 16
++ mtvsrdd 32+16, 0, 9
++ vmsumudm 5, 13, 14, 0
++ vmsumudm 5, 15, 16, 5
++
++ stxv 32+1, 16(3)
++ stxv 32+2, 32(3)
++ stxv 32+3, 48(3)
++ stxv 32+4, 64(3)
++
++ # out6
++ mtvsrdd 32+13, 11, 14
++ mtvsrdd 32+14, 15, 12
++ mtvsrdd 32+15, 9, 16
++ mtvsrdd 32+16, 9, 10
++ stxv 32+5, 80(3)
++ vmsumudm 19, 13, 14, 0
++ vmsumudm 6, 15, 16, 19
++
++ # out7
++ add 17, 9, 9
++ mtvsrdd 32+13, 11, 15
++ mtvsrdd 32+14, 16, 12
++ mtvsrdd 32+15, 0, 17
++ mtvsrdd 32+16, 0, 10
++ vmsumudm 19, 13, 14, 0
++ vmsumudm 7, 15, 16, 19
++
++ # out8
++ mtvsrdd 32+13, 11, 16
++ mtvsrdd 32+14, 17, 12
++ mtvsrdd 32+15, 0, 10
++ vmsumudm 19, 13, 14, 0
++ vmsumudm 8, 15, 15, 19
++
++ # out9
++ add 14, 10, 10
++ mtvsrdd 32+13, 11, 17
++ mtvsrdd 32+14, 14, 12
++ vmsumudm 9, 13, 14, 0
++
++ # out10
++ mtvsrdd 32+13, 11, 14
++ mtvsrdd 32+14, 11, 12
++ vmsumudm 10, 13, 14, 0
++
++ stxv 32+6, 96(3)
++ stxv 32+7, 112(3)
++
++ # out11
++ #add 14, 11, 11
++ #mtvsrdd 32+13, 0, 14
++ #mtvsrdd 32+14, 0, 12
++ #vmsumudm 11, 13, 14, 0
++
++ mulld 6, 12, 11
++ mulhdu 7, 12, 11
++ addc 8, 6, 6
++ adde 9, 7, 7
++
++ stxv 32+8, 128(3)
++ stxv 32+9, 144(3)
++ stxv 32+10, 160(3)
++ #stxv 32+11, 176(3)
++
++ # out12
++ mulld 14, 12, 12
++ mulhdu 15, 12, 12
++
++ std 8, 176(3)
++ std 9, 184(3)
++ std 14, 192(3)
++ std 15, 200(3)
++
++ blr
++.size _p384_felem_square_core,.-_p384_felem_square_core
++
++#
++# widefelem (128 bits) * 8
++#
++.macro F128_X_8 _off1 _off2
++ ld 9,\\_off1(3)
++ ld 8,\\_off2(3)
++ srdi 10,9,61
++ rldimi 10,8,3,0
++ sldi 9,9,3
++ std 9,\\_off1(3)
++ std 10,\\_off2(3)
++.endm
++
++.globl p384_felem128_mul_by_8
++.type p384_felem128_mul_by_8, \@function
++.align 4
++p384_felem128_mul_by_8:
++
++ F128_X_8 0, 8
++
++ F128_X_8 16, 24
++
++ F128_X_8 32, 40
++
++ F128_X_8 48, 56
++
++ F128_X_8 64, 72
++
++ F128_X_8 80, 88
++
++ F128_X_8 96, 104
++
++ F128_X_8 112, 120
++
++ F128_X_8 128, 136
++
++ F128_X_8 144, 152
++
++ F128_X_8 160, 168
++
++ F128_X_8 176, 184
++
++ F128_X_8 192, 200
++
++ blr
++.size p384_felem128_mul_by_8,.-p384_felem128_mul_by_8
++
++#
++# widefelem (128 bits) * 2
++#
++.macro F128_X_2 _off1 _off2
++ ld 9,\\_off1(3)
++ ld 8,\\_off2(3)
++ srdi 10,9,63
++ rldimi 10,8,1,0
++ sldi 9,9,1
++ std 9,\\_off1(3)
++ std 10,\\_off2(3)
++.endm
++
++.globl p384_felem128_mul_by_2
++.type p384_felem128_mul_by_2, \@function
++.align 4
++p384_felem128_mul_by_2:
++
++ F128_X_2 0, 8
++
++ F128_X_2 16, 24
++
++ F128_X_2 32, 40
++
++ F128_X_2 48, 56
++
++ F128_X_2 64, 72
++
++ F128_X_2 80, 88
++
++ F128_X_2 96, 104
++
++ F128_X_2 112, 120
++
++ F128_X_2 128, 136
++
++ F128_X_2 144, 152
++
++ F128_X_2 160, 168
++
++ F128_X_2 176, 184
++
++ F128_X_2 192, 200
++
++ blr
++.size p384_felem128_mul_by_2,.-p384_felem128_mul_by_2
++
++.globl p384_felem_diff128
++.type p384_felem_diff128, \@function
++.align 4
++p384_felem_diff128:
++
++ addis 5, 2, .LConst_two127\@toc\@ha
++ addi 5, 5, .LConst_two127\@toc\@l
++
++ ld 10, 0(3)
++ ld 8, 8(3)
++ li 9, 0
++ addc 10, 10, 9
++ li 7, -1
++ rldicr 7, 7, 0, 0 # two127
++ adde 8, 8, 7
++ ld 11, 0(4)
++ ld 12, 8(4)
++ subfc 11, 11, 10
++ subfe 12, 12, 8
++ std 11, 0(3) # out0
++ std 12, 8(3)
++
++ # two127m71 = (r10, r9)
++ ld 8, 16(3)
++ ld 7, 24(3)
++ ld 10, 24(5) # two127m71
++ addc 8, 8, 9
++ adde 7, 7, 10
++ ld 11, 16(4)
++ ld 12, 24(4)
++ subfc 11, 11, 8
++ subfe 12, 12, 7
++ std 11, 16(3) # out1
++ std 12, 24(3)
++
++ ld 8, 32(3)
++ ld 7, 40(3)
++ addc 8, 8, 9
++ adde 7, 7, 10
++ ld 11, 32(4)
++ ld 12, 40(4)
++ subfc 11, 11, 8
++ subfe 12, 12, 7
++ std 11, 32(3) # out2
++ std 12, 40(3)
++
++ ld 8, 48(3)
++ ld 7, 56(3)
++ addc 8, 8, 9
++ adde 7, 7, 10
++ ld 11, 48(4)
++ ld 12, 56(4)
++ subfc 11, 11, 8
++ subfe 12, 12, 7
++ std 11, 48(3) # out3
++ std 12, 56(3)
++
++ ld 8, 64(3)
++ ld 7, 72(3)
++ addc 8, 8, 9
++ adde 7, 7, 10
++ ld 11, 64(4)
++ ld 12, 72(4)
++ subfc 11, 11, 8
++ subfe 12, 12, 7
++ std 11, 64(3) # out4
++ std 12, 72(3)
++
++ ld 8, 80(3)
++ ld 7, 88(3)
++ addc 8, 8, 9
++ adde 7, 7, 10
++ ld 11, 80(4)
++ ld 12, 88(4)
++ subfc 11, 11, 8
++ subfe 12, 12, 7
++ std 11, 80(3) # out5
++ std 12, 88(3)
++
++ ld 8, 96(3)
++ ld 7, 104(3)
++ ld 6, 40(5) # two127p111m79m71
++ addc 8, 8, 9
++ adde 7, 7, 6
++ ld 11, 96(4)
++ ld 12, 104(4)
++ subfc 11, 11, 8
++ subfe 12, 12, 7
++ std 11, 96(3) # out6
++ std 12, 104(3)
++
++ ld 8, 112(3)
++ ld 7, 120(3)
++ ld 6, 56(5) # two127m119m71
++ addc 8, 8, 9
++ adde 7, 7, 6
++ ld 11, 112(4)
++ ld 12, 120(4)
++ subfc 11, 11, 8
++ subfe 12, 12, 7
++ std 11, 112(3) # out7
++ std 12, 120(3)
++
++ ld 8, 128(3)
++ ld 7, 136(3)
++ ld 6, 72(5) # two127m95m71
++ addc 8, 8, 9
++ adde 7, 7, 6
++ ld 11, 128(4)
++ ld 12, 136(4)
++ subfc 11, 11, 8
++ subfe 12, 12, 7
++ std 11, 128(3) # out8
++ std 12, 136(3)
++
++ ld 8, 144(3)
++ ld 7, 152(3)
++ addc 8, 8, 9
++ adde 7, 7, 10
++ ld 11, 144(4)
++ ld 12, 152(4)
++ subfc 11, 11, 8
++ subfe 12, 12, 7
++ std 11, 144(3) # out9
++ std 12, 152(3)
++
++ ld 8, 160(3)
++ ld 7, 168(3)
++ addc 8, 8, 9
++ adde 7, 7, 10
++ ld 11, 160(4)
++ ld 12, 168(4)
++ subfc 11, 11, 8
++ subfe 12, 12, 7
++ std 11, 160(3) # out10
++ std 12, 168(3)
++
++ ld 8, 176(3)
++ ld 7, 184(3)
++ addc 8, 8, 9
++ adde 7, 7, 10
++ ld 11, 176(4)
++ ld 12, 184(4)
++ subfc 11, 11, 8
++ subfe 12, 12, 7
++ std 11, 176(3) # out11
++ std 12, 184(3)
++
++ ld 8, 192(3)
++ ld 7, 200(3)
++ addc 8, 8, 9
++ adde 7, 7, 10
++ ld 11, 192(4)
++ ld 12, 200(4)
++ subfc 11, 11, 8
++ subfe 12, 12, 7
++ std 11, 192(3) # out12
++ std 12, 200(3)
++
++ blr
++.size p384_felem_diff128,.-p384_felem_diff128
++
++.data
++.align 4
++.LConst_two127:
++#two127
++.long 0x00000000, 0x00000000, 0x00000000, 0x80000000
++#two127m71
++.long 0x00000000, 0x00000000, 0xffffff80, 0x7fffffff
++#two127p111m79m71
++.long 0x00000000, 0x00000000, 0xffff7f80, 0x80007fff
++#two127m119m71
++.long 0x00000000, 0x00000000, 0xffffff80, 0x7f7fffff
++#two127m95m71
++.long 0x00000000, 0x00000000, 0x7fffff80, 0x7fffffff
++
++.text
++
++.globl p384_felem_diff_128_64
++.type p384_felem_diff_128_64, \@function
++.align 4
++p384_felem_diff_128_64:
++ addis 5, 2, .LConst_128_two64\@toc\@ha
++ addi 5, 5, .LConst_128_two64\@toc\@l
++
++ ld 9, 0(3)
++ ld 10, 8(3)
++ ld 8, 48(5) # two64p48m16
++ li 7, 0
++ addc 9, 9, 8
++ li 6, 1
++ adde 10, 10, 6
++ ld 11, 0(4)
++ subfc 8, 11, 9
++ subfe 12, 7, 10
++ std 8, 0(3) # out0
++ std 12, 8(3)
++
++ ld 9, 16(3)
++ ld 10, 24(3)
++ ld 8, 0(5) # two64m56m8
++ addc 9, 9, 8
++ addze 10, 10
++ ld 11, 8(4)
++ subfc 11, 11, 9
++ subfe 12, 7, 10
++ std 11, 16(3) # out1
++ std 12, 24(3)
++
++ ld 9, 32(3)
++ ld 10, 40(3)
++ ld 8, 16(5) # two64m32m8
++ addc 9, 9, 8
++ addze 10, 10
++ ld 11, 16(4)
++ subfc 11, 11, 9
++ subfe 12, 7, 10
++ std 11, 32(3) # out2
++ std 12, 40(3)
++
++ ld 10, 48(3)
++ ld 8, 56(3)
++ #ld 9, 32(5) # two64m8
++ li 9, -256 # two64m8
++ addc 10, 10, 9
++ addze 8, 8
++ ld 11, 24(4)
++ subfc 11, 11, 10
++ subfe 12, 7, 8
++ std 11, 48(3) # out3
++ std 12, 56(3)
++
++ ld 10, 64(3)
++ ld 8, 72(3)
++ addc 10, 10, 9
++ addze 8, 8
++ ld 11, 32(4)
++ subfc 11, 11, 10
++ subfe 12, 7, 8
++ std 11, 64(3) # out4
++ std 12, 72(3)
++
++ ld 10, 80(3)
++ ld 8, 88(3)
++ addc 10, 10, 9
++ addze 8, 8
++ ld 11, 40(4)
++ subfc 11, 11, 10
++ subfe 12, 7, 8
++ std 11, 80(3) # out5
++ std 12, 88(3)
++
++ ld 10, 96(3)
++ ld 8, 104(3)
++ addc 10, 10, 9
++ addze 9, 8
++ ld 11, 48(4)
++ subfc 11, 11, 10
++ subfe 12, 7, 9
++ std 11, 96(3) # out6
++ std 12, 104(3)
++
++ blr
++.size p384_felem_diff_128_64,.-p384_felem_diff_128_64
++
++.data
++.align 4
++.LConst_128_two64:
++#two64m56m8
++.long 0xffffff00, 0xfeffffff, 0x00000000, 0x00000000
++#two64m32m8
++.long 0xffffff00, 0xfffffffe, 0x00000000, 0x00000000
++#two64m8
++.long 0xffffff00, 0xffffffff, 0x00000000, 0x00000000
++#two64p48m16
++.long 0xffff0000, 0x0000ffff, 0x00000001, 0x00000000
++
++.LConst_two60:
++#two60m52m4
++.long 0xfffffff0, 0x0fefffff, 0x0, 0x0
++#two60p44m12
++.long 0xfffff000, 0x10000fff, 0x0, 0x0
++#two60m28m4
++.long 0xeffffff0, 0x0fffffff, 0x0, 0x0
++#two60m4
++.long 0xfffffff0, 0x0fffffff, 0x0, 0x0
++
++.text
++#
++# static void felem_diff64(felem out, const felem in)
++#
++.globl p384_felem_diff64
++.type p384_felem_diff64, \@function
++.align 4
++p384_felem_diff64:
++ addis 5, 2, .LConst_two60\@toc\@ha
++ addi 5, 5, .LConst_two60\@toc\@l
++
++ ld 9, 0(3)
++ ld 8, 16(5) # two60p44m12
++ li 7, 0
++ add 9, 9, 8
++ ld 11, 0(4)
++ subf 8, 11, 9
++ std 8, 0(3) # out0
++
++ ld 9, 8(3)
++ ld 8, 0(5) # two60m52m4
++ add 9, 9, 8
++ ld 11, 8(4)
++ subf 11, 11, 9
++ std 11, 8(3) # out1
++
++ ld 9, 16(3)
++ ld 8, 32(5) # two60m28m4
++ add 9, 9, 8
++ ld 11, 16(4)
++ subf 11, 11, 9
++ std 11, 16(3) # out2
++
++ ld 10, 24(3)
++ ld 9, 48(5) # two60m4
++ add 10, 10, 9
++ ld 12, 24(4)
++ subf 12, 12, 10
++ std 12, 24(3) # out3
++
++ ld 10, 32(3)
++ add 10, 10, 9
++ ld 11, 32(4)
++ subf 11, 11, 10
++ std 11, 32(3) # out4
++
++ ld 10, 40(3)
++ add 10, 10, 9
++ ld 12, 40(4)
++ subf 12, 12, 10
++ std 12, 40(3) # out5
+
+- endproc("p384_felem_square");
+- }
+-}
++ ld 10, 48(3)
++ add 10, 10, 9
++ ld 11, 48(4)
++ subf 11, 11, 10
++ std 11, 48(3) # out6
++
++ blr
++.size p384_felem_diff64,.-p384_felem_diff64
++
++.text
++#
++# Shift 128 bits right <nbits>
++#
++.macro SHR o_h o_l in_h in_l nbits
++ srdi \\o_l, \\in_l, \\nbits # shift lower right <nbits>
++ rldimi \\o_l, \\in_h, 64-\\nbits, 0 # insert <64-nbits> from hi
++ srdi \\o_h, \\in_h, \\nbits # shift higher right <nbits>
++.endm
++
++#
++# static void felem_reduce(felem out, const widefelem in)
++#
++.global p384_felem_reduce
++.type p384_felem_reduce,\@function
++.align 4
++p384_felem_reduce:
++
++ stdu 1, -208(1)
++ mflr 0
++ std 14, 56(1)
++ std 15, 64(1)
++ std 16, 72(1)
++ std 17, 80(1)
++ std 18, 88(1)
++ std 19, 96(1)
++ std 20, 104(1)
++ std 21, 112(1)
++ std 22, 120(1)
++ std 23, 128(1)
++ std 24, 136(1)
++ std 25, 144(1)
++ std 26, 152(1)
++ std 27, 160(1)
++ std 28, 168(1)
++ std 29, 176(1)
++ std 30, 184(1)
++ std 31, 192(1)
++
++ bl _p384_felem_reduce_core
++
++ mtlr 0
++ ld 14, 56(1)
++ ld 15, 64(1)
++ ld 16, 72(1)
++ ld 17, 80(1)
++ ld 18, 88(1)
++ ld 19, 96(1)
++ ld 20, 104(1)
++ ld 21, 112(1)
++ ld 22, 120(1)
++ ld 23, 128(1)
++ ld 24, 136(1)
++ ld 25, 144(1)
++ ld 26, 152(1)
++ ld 27, 160(1)
++ ld 28, 168(1)
++ ld 29, 176(1)
++ ld 30, 184(1)
++ ld 31, 192(1)
++ addi 1, 1, 208
++ blr
++.size p384_felem_reduce,.-p384_felem_reduce
++
++#
++# Felem reduction core function -
++# r3 and r4 need to pre-loaded.
++#
++.type _p384_felem_reduce_core,\@function
++.align 4
++_p384_felem_reduce_core:
++ addis 12, 2, .LConst\@toc\@ha
++ addi 12, 12, .LConst\@toc\@l
++
++ # load constat p
++ ld 11, 8(12) # hi - two124m68
++
++ # acc[6] = in[6] + two124m68;
++ ld 26, 96(4) # in[6].l
++ ld 27, 96+8(4) # in[6].h
++ add 27, 27, 11
++
++ # acc[5] = in[5] + two124m68;
++ ld 24, 80(4) # in[5].l
++ ld 25, 80+8(4) # in[5].h
++ add 25, 25, 11
++
++ # acc[4] = in[4] + two124m68;
++ ld 22, 64(4) # in[4].l
++ ld 23, 64+8(4) # in[4].h
++ add 23, 23, 11
++
++ # acc[3] = in[3] + two124m68;
++ ld 20, 48(4) # in[3].l
++ ld 21, 48+8(4) # in[3].h
++ add 21, 21, 11
++
++ ld 11, 48+8(12) # hi - two124m92m68
++
++ # acc[2] = in[2] + two124m92m68;
++ ld 18, 32(4) # in[2].l
++ ld 19, 32+8(4) # in[2].h
++ add 19, 19, 11
++
++ ld 11, 16+8(12) # high - two124m116m68
++
++ # acc[1] = in[1] + two124m116m68;
++ ld 16, 16(4) # in[1].l
++ ld 17, 16+8(4) # in[1].h
++ add 17, 17, 11
++
++ ld 11, 32+8(12) # high - two124p108m76
++
++ # acc[0] = in[0] + two124p108m76;
++ ld 14, 0(4) # in[0].l
++ ld 15, 0+8(4) # in[0].h
++ add 15, 15, 11
++
++ # compute mask
++ li 7, -1
++
++ # Eliminate in[12]
++
++ # acc[8] += in[12] >> 32;
++ ld 5, 192(4) # in[12].l
++ ld 6, 192+8(4) # in[12].h
++ SHR 9, 10, 6, 5, 32
++ ld 30, 128(4) # in[8].l
++ ld 31, 136(4) # in[8].h
++ addc 30, 30, 10
++ adde 31, 31, 9
++
++ # acc[7] += (in[12] & 0xffffffff) << 24;
++ srdi 11, 7, 32 # 0xffffffff
++ and 11, 11, 5
++ sldi 11, 11, 24 # << 24
++ ld 28, 112(4) # in[7].l
++ ld 29, 120(4) # in[7].h
++ addc 28, 28, 11
++ addze 29, 29
++
++ # acc[7] += in[12] >> 8;
++ SHR 9, 10, 6, 5, 8
++ addc 28, 28, 10
++ adde 29, 29, 9
++
++ # acc[6] += (in[12] & 0xff) << 48;
++ andi. 11, 5, 0xff
++ sldi 11, 11, 48
++ addc 26, 26, 11
++ addze 27, 27
++
++ # acc[6] -= in[12] >> 16;
++ SHR 9, 10, 6, 5, 16
++ subfc 26, 10, 26
++ subfe 27, 9, 27
++
++ # acc[5] -= (in[12] & 0xffff) << 40;
++ srdi 11, 7, 48 # 0xffff
++ and 11, 11, 5
++ sldi 11, 11, 40 # << 40
++ li 9, 0
++ subfc 24, 11, 24
++ subfe 25, 9, 25
++
++ # acc[6] += in[12] >> 48;
++ SHR 9, 10, 6, 5, 48
++ addc 26, 26, 10
++ adde 27, 27, 9
++
++ # acc[5] += (in[12] & 0xffffffffffff) << 8;
++ srdi 11, 7, 16 # 0xffffffffffff
++ and 11, 11, 5
++ sldi 11, 11, 8 # << 8
++ addc 24, 24, 11
++ addze 25, 25
++
++ # Eliminate in[11]
++
++ # acc[7] += in[11] >> 32;
++ ld 5, 176(4) # in[11].l
++ ld 6, 176+8(4) # in[11].h
++ SHR 9, 10, 6, 5, 32
++ addc 28, 28, 10
++ adde 29, 29, 9
++
++ # acc[6] += (in[11] & 0xffffffff) << 24;
++ srdi 11, 7, 32 # 0xffffffff
++ and 11, 11, 5
++ sldi 11, 11, 24 # << 24
++ addc 26, 26, 11
++ addze 27, 27
++
++ # acc[6] += in[11] >> 8;
++ SHR 9, 10, 6, 5, 8
++ addc 26, 26, 10
++ adde 27, 27, 9
++
++ # acc[5] += (in[11] & 0xff) << 48;
++ andi. 11, 5, 0xff
++ sldi 11, 11, 48
++ addc 24, 24, 11
++ addze 25, 25
++
++ # acc[5] -= in[11] >> 16;
++ SHR 9, 10, 6, 5, 16
++ subfc 24, 10, 24
++ subfe 25, 9, 25
++
++ # acc[4] -= (in[11] & 0xffff) << 40;
++ srdi 11, 7, 48 # 0xffff
++ and 11, 11, 5
++ sldi 11, 11, 40 # << 40
++ li 9, 0
++ subfc 22, 11, 22
++ subfe 23, 9, 23
++
++ # acc[5] += in[11] >> 48;
++ SHR 9, 10, 6, 5, 48
++ addc 24, 24, 10
++ adde 25, 25, 9
++
++ # acc[4] += (in[11] & 0xffffffffffff) << 8;
++ srdi 11, 7, 16 # 0xffffffffffff
++ and 11, 11, 5
++ sldi 11, 11, 8 # << 8
++ addc 22, 22, 11
++ addze 23, 23
++
++ # Eliminate in[10]
++
++ # acc[6] += in[10] >> 32;
++ ld 5, 160(4) # in[10].l
++ ld 6, 160+8(4) # in[10].h
++ SHR 9, 10, 6, 5, 32
++ addc 26, 26, 10
++ adde 27, 27, 9
++
++ # acc[5] += (in[10] & 0xffffffff) << 24;
++ srdi 11, 7, 32 # 0xffffffff
++ and 11, 11, 5
++ sldi 11, 11, 24 # << 24
++ addc 24, 24, 11
++ addze 25, 25
++
++ # acc[5] += in[10] >> 8;
++ SHR 9, 10, 6, 5, 8
++ addc 24, 24, 10
++ adde 25, 25, 9
++
++ # acc[4] += (in[10] & 0xff) << 48;
++ andi. 11, 5, 0xff
++ sldi 11, 11, 48
++ addc 22, 22, 11
++ addze 23, 23
++
++ # acc[4] -= in[10] >> 16;
++ SHR 9, 10, 6, 5, 16
++ subfc 22, 10, 22
++ subfe 23, 9, 23
++
++ # acc[3] -= (in[10] & 0xffff) << 40;
++ srdi 11, 7, 48 # 0xffff
++ and 11, 11, 5
++ sldi 11, 11, 40 # << 40
++ li 9, 0
++ subfc 20, 11, 20
++ subfe 21, 9, 21
++
++ # acc[4] += in[10] >> 48;
++ SHR 9, 10, 6, 5, 48
++ addc 22, 22, 10
++ adde 23, 23, 9
++
++ # acc[3] += (in[10] & 0xffffffffffff) << 8;
++ srdi 11, 7, 16 # 0xffffffffffff
++ and 11, 11, 5
++ sldi 11, 11, 8 # << 8
++ addc 20, 20, 11
++ addze 21, 21
++
++ # Eliminate in[9]
++
++ # acc[5] += in[9] >> 32;
++ ld 5, 144(4) # in[9].l
++ ld 6, 144+8(4) # in[9].h
++ SHR 9, 10, 6, 5, 32
++ addc 24, 24, 10
++ adde 25, 25, 9
++
++ # acc[4] += (in[9] & 0xffffffff) << 24;
++ srdi 11, 7, 32 # 0xffffffff
++ and 11, 11, 5
++ sldi 11, 11, 24 # << 24
++ addc 22, 22, 11
++ addze 23, 23
++
++ # acc[4] += in[9] >> 8;
++ SHR 9, 10, 6, 5, 8
++ addc 22, 22, 10
++ adde 23, 23, 9
++
++ # acc[3] += (in[9] & 0xff) << 48;
++ andi. 11, 5, 0xff
++ sldi 11, 11, 48
++ addc 20, 20, 11
++ addze 21, 21
++
++ # acc[3] -= in[9] >> 16;
++ SHR 9, 10, 6, 5, 16
++ subfc 20, 10, 20
++ subfe 21, 9, 21
++
++ # acc[2] -= (in[9] & 0xffff) << 40;
++ srdi 11, 7, 48 # 0xffff
++ and 11, 11, 5
++ sldi 11, 11, 40 # << 40
++ li 9, 0
++ subfc 18, 11, 18
++ subfe 19, 9, 19
++
++ # acc[3] += in[9] >> 48;
++ SHR 9, 10, 6, 5, 48
++ addc 20, 20, 10
++ adde 21, 21, 9
++
++ # acc[2] += (in[9] & 0xffffffffffff) << 8;
++ srdi 11, 7, 16 # 0xffffffffffff
++ and 11, 11, 5
++ sldi 11, 11, 8 # << 8
++ addc 18, 18, 11
++ addze 19, 19
++
++ # Eliminate acc[8]
++
++ # acc[4] += acc[8] >> 32;
++ mr 5, 30 # acc[8].l
++ mr 6, 31 # acc[8].h
++ SHR 9, 10, 6, 5, 32
++ addc 22, 22, 10
++ adde 23, 23, 9
++
++ # acc[3] += (acc[8] & 0xffffffff) << 24;
++ srdi 11, 7, 32 # 0xffffffff
++ and 11, 11, 5
++ sldi 11, 11, 24 # << 24
++ addc 20, 20, 11
++ addze 21, 21
++
++ # acc[3] += acc[8] >> 8;
++ SHR 9, 10, 6, 5, 8
++ addc 20, 20, 10
++ adde 21, 21, 9
++
++ # acc[2] += (acc[8] & 0xff) << 48;
++ andi. 11, 5, 0xff
++ sldi 11, 11, 48
++ addc 18, 18, 11
++ addze 19, 19
++
++ # acc[2] -= acc[8] >> 16;
++ SHR 9, 10, 6, 5, 16
++ subfc 18, 10, 18
++ subfe 19, 9, 19
++
++ # acc[1] -= (acc[8] & 0xffff) << 40;
++ srdi 11, 7, 48 # 0xffff
++ and 11, 11, 5
++ sldi 11, 11, 40 # << 40
++ li 9, 0
++ subfc 16, 11, 16
++ subfe 17, 9, 17
++
++ #acc[2] += acc[8] >> 48;
++ SHR 9, 10, 6, 5, 48
++ addc 18, 18, 10
++ adde 19, 19, 9
++
++ # acc[1] += (acc[8] & 0xffffffffffff) << 8;
++ srdi 11, 7, 16 # 0xffffffffffff
++ and 11, 11, 5
++ sldi 11, 11, 8 # << 8
++ addc 16, 16, 11
++ addze 17, 17
++
++ # Eliminate acc[7]
++
++ # acc[3] += acc[7] >> 32;
++ mr 5, 28 # acc[7].l
++ mr 6, 29 # acc[7].h
++ SHR 9, 10, 6, 5, 32
++ addc 20, 20, 10
++ adde 21, 21, 9
++
++ # acc[2] += (acc[7] & 0xffffffff) << 24;
++ srdi 11, 7, 32 # 0xffffffff
++ and 11, 11, 5
++ sldi 11, 11, 24 # << 24
++ addc 18, 18, 11
++ addze 19, 19
++
++ # acc[2] += acc[7] >> 8;
++ SHR 9, 10, 6, 5, 8
++ addc 18, 18, 10
++ adde 19, 19, 9
++
++ # acc[1] += (acc[7] & 0xff) << 48;
++ andi. 11, 5, 0xff
++ sldi 11, 11, 48
++ addc 16, 16, 11
++ addze 17, 17
++
++ # acc[1] -= acc[7] >> 16;
++ SHR 9, 10, 6, 5, 16
++ subfc 16, 10, 16
++ subfe 17, 9, 17
++
++ # acc[0] -= (acc[7] & 0xffff) << 40;
++ srdi 11, 7, 48 # 0xffff
++ and 11, 11, 5
++ sldi 11, 11, 40 # << 40
++ li 9, 0
++ subfc 14, 11, 14
++ subfe 15, 9, 15
++
++ # acc[1] += acc[7] >> 48;
++ SHR 9, 10, 6, 5, 48
++ addc 16, 16, 10
++ adde 17, 17, 9
++
++ # acc[0] += (acc[7] & 0xffffffffffff) << 8;
++ srdi 11, 7, 16 # 0xffffffffffff
++ and 11, 11, 5
++ sldi 11, 11, 8 # << 8
++ addc 14, 14, 11
++ addze 15, 15
++
++ #
++ # Carry 4 -> 5 -> 6
++ #
++ # acc[5] += acc[4] >> 56;
++ # acc[4] &= 0x00ffffffffffffff;
++ SHR 9, 10, 23, 22, 56
++ addc 24, 24, 10
++ adde 25, 25, 9
++ srdi 11, 7, 8 # 0x00ffffffffffffff
++ and 22, 22, 11
++ li 23, 0
++
++ # acc[6] += acc[5] >> 56;
++ # acc[5] &= 0x00ffffffffffffff;
++ SHR 9, 10, 25, 24, 56
++ addc 26, 26, 10
++ adde 27, 27, 9
++ and 24, 24, 11
++ li 25, 0
++
++ # [3]: Eliminate high bits of acc[6] */
++ # temp = acc[6] >> 48;
++ # acc[6] &= 0x0000ffffffffffff;
++ SHR 31, 30, 27, 26, 48 # temp = acc[6] >> 48
++ srdi 11, 7, 16 # 0x0000ffffffffffff
++ and 26, 26, 11
++ li 27, 0
++
++ # temp < 2^80
++ # acc[3] += temp >> 40;
++ SHR 9, 10, 31, 30, 40
++ addc 20, 20, 10
++ adde 21, 21, 9
++
++ # acc[2] += (temp & 0xffffffffff) << 16;
++ srdi 11, 7, 24 # 0xffffffffff
++ and 10, 30, 11
++ sldi 10, 10, 16
++ addc 18, 18, 10
++ addze 19, 19
++
++ # acc[2] += temp >> 16;
++ SHR 9, 10, 31, 30, 16
++ addc 18, 18, 10
++ adde 19, 19, 9
++
++ # acc[1] += (temp & 0xffff) << 40;
++ srdi 11, 7, 48 # 0xffff
++ and 10, 30, 11
++ sldi 10, 10, 40
++ addc 16, 16, 10
++ addze 17, 17
++
++ # acc[1] -= temp >> 24;
++ SHR 9, 10, 31, 30, 24
++ subfc 16, 10, 16
++ subfe 17, 9, 17
++
++ # acc[0] -= (temp & 0xffffff) << 32;
++ srdi 11, 7, 40 # 0xffffff
++ and 10, 30, 11
++ sldi 10, 10, 32
++ li 9, 0
++ subfc 14, 10, 14
++ subfe 15, 9, 15
++
++ # acc[0] += temp;
++ addc 14, 14, 30
++ adde 15, 15, 31
++
++ # Carry 0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6
++ #
++ # acc[1] += acc[0] >> 56; /* acc[1] < acc_old[1] + 2^72 */
++ SHR 9, 10, 15, 14, 56
++ addc 16, 16, 10
++ adde 17, 17, 9
++
++ # acc[0] &= 0x00ffffffffffffff;
++ srdi 11, 7, 8 # 0x00ffffffffffffff
++ and 14, 14, 11
++ li 15, 0
++
++ # acc[2] += acc[1] >> 56; /* acc[2] < acc_old[2] + 2^72 + 2^16 */
++ SHR 9, 10, 17, 16, 56
++ addc 18, 18, 10
++ adde 19, 19, 9
++
++ # acc[1] &= 0x00ffffffffffffff;
++ and 16, 16, 11
++ li 17, 0
++
++ # acc[3] += acc[2] >> 56; /* acc[3] < acc_old[3] + 2^72 + 2^16 */
++ SHR 9, 10, 19, 18, 56
++ addc 20, 20, 10
++ adde 21, 21, 9
++
++ # acc[2] &= 0x00ffffffffffffff;
++ and 18, 18, 11
++ li 19, 0
++
++ # acc[4] += acc[3] >> 56;
++ SHR 9, 10, 21, 20, 56
++ addc 22, 22, 10
++ adde 23, 23, 9
++
++ # acc[3] &= 0x00ffffffffffffff;
++ and 20, 20, 11
++ li 21, 0
++
++ # acc[5] += acc[4] >> 56;
++ SHR 9, 10, 23, 22, 56
++ addc 24, 24, 10
++ adde 25, 25, 9
++
++ # acc[4] &= 0x00ffffffffffffff;
++ and 22, 22, 11
++
++ # acc[6] += acc[5] >> 56;
++ SHR 9, 10, 25, 24, 56
++ addc 26, 26, 10
++ adde 27, 27, 9
++
++ # acc[5] &= 0x00ffffffffffffff;
++ and 24, 24, 11
++
++ std 14, 0(3)
++ std 16, 8(3)
++ std 18, 16(3)
++ std 20, 24(3)
++ std 22, 32(3)
++ std 24, 40(3)
++ std 26, 48(3)
++ blr
++.size _p384_felem_reduce_core,.-_p384_felem_reduce_core
++
++.data
++.align 4
++.LConst:
++# two124m68:
++.long 0x0, 0x0, 0xfffffff0, 0xfffffff
++# two124m116m68:
++.long 0x0, 0x0, 0xfffffff0, 0xfefffff
++#two124p108m76:
++.long 0x0, 0x0, 0xfffff000, 0x10000fff
++#two124m92m68:
++.long 0x0, 0x0, 0xeffffff0, 0xfffffff
++
++.text
++
++#
++# void p384_felem_square_reduce(felem out, const felem in)
++#
++.global p384_felem_square_reduce
++.type p384_felem_square_reduce,\@function
++.align 4
++p384_felem_square_reduce:
++ stdu 1, -512(1)
++ mflr 0
++ std 14, 56(1)
++ std 15, 64(1)
++ std 16, 72(1)
++ std 17, 80(1)
++ std 18, 88(1)
++ std 19, 96(1)
++ std 20, 104(1)
++ std 21, 112(1)
++ std 22, 120(1)
++ std 23, 128(1)
++ std 24, 136(1)
++ std 25, 144(1)
++ std 26, 152(1)
++ std 27, 160(1)
++ std 28, 168(1)
++ std 29, 176(1)
++ std 30, 184(1)
++ std 31, 192(1)
++
++ std 3, 496(1)
++ addi 3, 1, 208
++ bl _p384_felem_square_core
++
++ mr 4, 3
++ ld 3, 496(1)
++ bl _p384_felem_reduce_core
++
++ ld 14, 56(1)
++ ld 15, 64(1)
++ ld 16, 72(1)
++ ld 17, 80(1)
++ ld 18, 88(1)
++ ld 19, 96(1)
++ ld 20, 104(1)
++ ld 21, 112(1)
++ ld 22, 120(1)
++ ld 23, 128(1)
++ ld 24, 136(1)
++ ld 25, 144(1)
++ ld 26, 152(1)
++ ld 27, 160(1)
++ ld 28, 168(1)
++ ld 29, 176(1)
++ ld 30, 184(1)
++ ld 31, 192(1)
++ addi 1, 1, 512
++ mtlr 0
++ blr
++.size p384_felem_square_reduce,.-p384_felem_square_reduce
++
++#
++# void p384_felem_mul_reduce(felem out, const felem in1, const felem in2)
++#
++.global p384_felem_mul_reduce
++.type p384_felem_mul_reduce,\@function
++.align 5
++p384_felem_mul_reduce:
++ stdu 1, -512(1)
++ mflr 0
++ std 14, 56(1)
++ std 15, 64(1)
++ std 16, 72(1)
++ std 17, 80(1)
++ std 18, 88(1)
++ std 19, 96(1)
++ std 20, 104(1)
++ std 21, 112(1)
++ std 22, 120(1)
++ std 23, 128(1)
++ std 24, 136(1)
++ std 25, 144(1)
++ std 26, 152(1)
++ std 27, 160(1)
++ std 28, 168(1)
++ std 29, 176(1)
++ std 30, 184(1)
++ std 31, 192(1)
++
++ std 3, 496(1)
++ addi 3, 1, 208
++ bl _p384_felem_mul_core
++
++ mr 4, 3
++ ld 3, 496(1)
++ bl _p384_felem_reduce_core
++
++ ld 14, 56(1)
++ ld 15, 64(1)
++ ld 16, 72(1)
++ ld 17, 80(1)
++ ld 18, 88(1)
++ ld 19, 96(1)
++ ld 20, 104(1)
++ ld 21, 112(1)
++ ld 22, 120(1)
++ ld 23, 128(1)
++ ld 24, 136(1)
++ ld 25, 144(1)
++ ld 26, 152(1)
++ ld 27, 160(1)
++ ld 28, 168(1)
++ ld 29, 176(1)
++ ld 30, 184(1)
++ ld 31, 192(1)
++ addi 1, 1, 512
++ mtlr 0
++ blr
++.size p384_felem_mul_reduce,.-p384_felem_mul_reduce
++___
+
+ $code =~ s/\`([^\`]*)\`/eval $1/gem;
+ print $code;
+diff --git a/crypto/ec/ecp_nistp384.c b/crypto/ec/ecp_nistp384.c
+index 3fd7a40020..e0b5786bc1 100644
+--- a/crypto/ec/ecp_nistp384.c
++++ b/crypto/ec/ecp_nistp384.c
+@@ -252,6 +252,16 @@ static void felem_neg(felem out, const felem in)
+ out[6] = two60m4 - in[6];
+ }
+
++#if defined(ECP_NISTP384_ASM)
++void p384_felem_diff64(felem out, const felem in);
++void p384_felem_diff128(widefelem out, const widefelem in);
++void p384_felem_diff_128_64(widefelem out, const felem in);
++
++# define felem_diff64 p384_felem_diff64
++# define felem_diff128 p384_felem_diff128
++# define felem_diff_128_64 p384_felem_diff_128_64
++
++#else
+ /*-
+ * felem_diff64 subtracts |in| from |out|
+ * On entry:
+@@ -369,6 +379,7 @@ static void felem_diff128(widefelem out, const widefelem in)
+ for (i = 0; i < 2*NLIMBS-1; i++)
+ out[i] -= in[i];
+ }
++#endif /* ECP_NISTP384_ASM */
+
+ static void felem_square_ref(widefelem out, const felem in)
+ {
+@@ -503,7 +514,7 @@ static void felem_mul_ref(widefelem out, const felem in1, const felem in2)
+ * [3]: Y = 2^48 (acc[6] >> 48)
+ * (Where a | b | c | d = (2^56)^3 a + (2^56)^2 b + (2^56) c + d)
+ */
+-static void felem_reduce(felem out, const widefelem in)
++static void felem_reduce_ref(felem out, const widefelem in)
+ {
+ /*
+ * In order to prevent underflow, we add a multiple of p before subtracting.
+@@ -682,8 +693,11 @@ static void (*felem_square_p)(widefelem out, const felem in) =
+ static void (*felem_mul_p)(widefelem out, const felem in1, const felem in2) =
+ felem_mul_wrapper;
+
++static void (*felem_reduce_p)(felem out, const widefelem in) = felem_reduce_ref;
++
+ void p384_felem_square(widefelem out, const felem in);
+ void p384_felem_mul(widefelem out, const felem in1, const felem in2);
++void p384_felem_reduce(felem out, const widefelem in);
+
+ # if defined(_ARCH_PPC64)
+ # include "crypto/ppc_arch.h"
+@@ -695,6 +709,7 @@ static void felem_select(void)
+ if ((OPENSSL_ppccap_P & PPC_MADD300) && (OPENSSL_ppccap_P & PPC_ALTIVEC)) {
+ felem_square_p = p384_felem_square;
+ felem_mul_p = p384_felem_mul;
++ felem_reduce_p = p384_felem_reduce;
+
+ return;
+ }
+@@ -703,6 +718,7 @@ static void felem_select(void)
+ /* Default */
+ felem_square_p = felem_square_ref;
+ felem_mul_p = felem_mul_ref;
++ felem_reduce_p = p384_felem_reduce;
+ }
+
+ static void felem_square_wrapper(widefelem out, const felem in)
+@@ -719,10 +735,17 @@ static void felem_mul_wrapper(widefelem out, const felem in1, const felem in2)
+
+ # define felem_square felem_square_p
+ # define felem_mul felem_mul_p
++# define felem_reduce felem_reduce_p
++
++void p384_felem_square_reduce(felem out, const felem in);
++void p384_felem_mul_reduce(felem out, const felem in1, const felem in2);
++
++# define felem_square_reduce p384_felem_square_reduce
++# define felem_mul_reduce p384_felem_mul_reduce
+ #else
+ # define felem_square felem_square_ref
+ # define felem_mul felem_mul_ref
+-#endif
++# define felem_reduce felem_reduce_ref
+
+ static ossl_inline void felem_square_reduce(felem out, const felem in)
+ {
+@@ -739,6 +762,7 @@ static ossl_inline void felem_mul_reduce(felem out, const felem in1, const felem
+ felem_mul(tmp, in1, in2);
+ felem_reduce(out, tmp);
+ }
++#endif
+
+ /*-
+ * felem_inv calculates |out| = |in|^{-1}
diff --git a/meta/recipes-connectivity/openssl/openssl/CVE-2025-27587-2.patch b/meta/recipes-connectivity/openssl/openssl/CVE-2025-27587-2.patch
new file mode 100644
index 0000000000..0659a9d6d9
--- /dev/null
+++ b/meta/recipes-connectivity/openssl/openssl/CVE-2025-27587-2.patch
@@ -0,0 +1,129 @@
+From 6b1646e472c9e8c08bb14066ba2a7c3eed45f84a Mon Sep 17 00:00:00 2001
+From: "A. Wilcox" <AWilcox@Wilcox-Tech.com>
+Date: Thu, 17 Apr 2025 08:51:53 -0500
+Subject: [PATCH] Fix P-384 curve on lower-than-P9 PPC64 targets
+
+The change adding an asm implementation of p384_felem_reduce incorrectly
+uses the accelerated version on both targets that support the intrinsics
+*and* targets that don't, instead of falling back to the generics on older
+targets. This results in crashes when trying to use P-384 on < Power9.
+
+Signed-off-by: Anna Wilcox <AWilcox@Wilcox-Tech.com>
+Closes: #27350
+Fixes: 85cabd94 ("Fix Minerva timing side-channel signal for P-384 curve on PPC")
+
+Reviewed-by: Dmitry Belyavskiy <beldmit@gmail.com>
+Reviewed-by: Tomas Mraz <tomas@openssl.org>
+(Merged from https://github.com/openssl/openssl/pull/27429)
+
+(cherry picked from commit 29864f2b0f1046177e8048a5b17440893d3f9425)
+
+CVE: CVE-2025-27587
+Upstream-Status: Backport [https://github.com/openssl/openssl/commit/6b1646e472c9e8c08bb14066ba2a7c3eed45f84a]
+Signed-off-by: Peter Marko <peter.marko@siemens.com>
+---
+ crypto/ec/ecp_nistp384.c | 54 ++++++++++++++++++++++++----------------
+ 1 file changed, 33 insertions(+), 21 deletions(-)
+
+diff --git a/crypto/ec/ecp_nistp384.c b/crypto/ec/ecp_nistp384.c
+index e0b5786bc1..439b4d03a3 100644
+--- a/crypto/ec/ecp_nistp384.c
++++ b/crypto/ec/ecp_nistp384.c
+@@ -684,6 +684,22 @@ static void felem_reduce_ref(felem out, const widefelem in)
+ out[i] = acc[i];
+ }
+
++static ossl_inline void felem_square_reduce_ref(felem out, const felem in)
++{
++ widefelem tmp;
++
++ felem_square_ref(tmp, in);
++ felem_reduce_ref(out, tmp);
++}
++
++static ossl_inline void felem_mul_reduce_ref(felem out, const felem in1, const felem in2)
++{
++ widefelem tmp;
++
++ felem_mul_ref(tmp, in1, in2);
++ felem_reduce_ref(out, tmp);
++}
++
+ #if defined(ECP_NISTP384_ASM)
+ static void felem_square_wrapper(widefelem out, const felem in);
+ static void felem_mul_wrapper(widefelem out, const felem in1, const felem in2);
+@@ -695,10 +711,18 @@ static void (*felem_mul_p)(widefelem out, const felem in1, const felem in2) =
+
+ static void (*felem_reduce_p)(felem out, const widefelem in) = felem_reduce_ref;
+
++static void (*felem_square_reduce_p)(felem out, const felem in) =
++ felem_square_reduce_ref;
++static void (*felem_mul_reduce_p)(felem out, const felem in1, const felem in2) =
++ felem_mul_reduce_ref;
++
+ void p384_felem_square(widefelem out, const felem in);
+ void p384_felem_mul(widefelem out, const felem in1, const felem in2);
+ void p384_felem_reduce(felem out, const widefelem in);
+
++void p384_felem_square_reduce(felem out, const felem in);
++void p384_felem_mul_reduce(felem out, const felem in1, const felem in2);
++
+ # if defined(_ARCH_PPC64)
+ # include "crypto/ppc_arch.h"
+ # endif
+@@ -710,6 +734,8 @@ static void felem_select(void)
+ felem_square_p = p384_felem_square;
+ felem_mul_p = p384_felem_mul;
+ felem_reduce_p = p384_felem_reduce;
++ felem_square_reduce_p = p384_felem_square_reduce;
++ felem_mul_reduce_p = p384_felem_mul_reduce;
+
+ return;
+ }
+@@ -718,7 +744,9 @@ static void felem_select(void)
+ /* Default */
+ felem_square_p = felem_square_ref;
+ felem_mul_p = felem_mul_ref;
+- felem_reduce_p = p384_felem_reduce;
++ felem_reduce_p = felem_reduce_ref;
++ felem_square_reduce_p = felem_square_reduce_ref;
++ felem_mul_reduce_p = felem_mul_reduce_ref;
+ }
+
+ static void felem_square_wrapper(widefelem out, const felem in)
+@@ -737,31 +765,15 @@ static void felem_mul_wrapper(widefelem out, const felem in1, const felem in2)
+ # define felem_mul felem_mul_p
+ # define felem_reduce felem_reduce_p
+
+-void p384_felem_square_reduce(felem out, const felem in);
+-void p384_felem_mul_reduce(felem out, const felem in1, const felem in2);
+-
+-# define felem_square_reduce p384_felem_square_reduce
+-# define felem_mul_reduce p384_felem_mul_reduce
++# define felem_square_reduce felem_square_reduce_p
++# define felem_mul_reduce felem_mul_reduce_p
+ #else
+ # define felem_square felem_square_ref
+ # define felem_mul felem_mul_ref
+ # define felem_reduce felem_reduce_ref
+
+-static ossl_inline void felem_square_reduce(felem out, const felem in)
+-{
+- widefelem tmp;
+-
+- felem_square(tmp, in);
+- felem_reduce(out, tmp);
+-}
+-
+-static ossl_inline void felem_mul_reduce(felem out, const felem in1, const felem in2)
+-{
+- widefelem tmp;
+-
+- felem_mul(tmp, in1, in2);
+- felem_reduce(out, tmp);
+-}
++# define felem_square_reduce felem_square_reduce_ref
++# define felem_mul_reduce felem_mul_reduce_ref
+ #endif
+
+ /*-
diff --git a/meta/recipes-connectivity/openssl/openssl_3.2.4.bb b/meta/recipes-connectivity/openssl/openssl_3.2.4.bb
index d6bf32d989..fd98b32007 100644
--- a/meta/recipes-connectivity/openssl/openssl_3.2.4.bb
+++ b/meta/recipes-connectivity/openssl/openssl_3.2.4.bb
@@ -13,6 +13,8 @@ SRC_URI = "https://github.com/openssl/openssl/releases/download/openssl-${PV}/op
file://0001-Configure-do-not-tweak-mips-cflags.patch \
file://0001-Added-handshake-history-reporting-when-test-fails.patch \
file://CVE-2024-41996.patch \
+ file://CVE-2025-27587-1.patch \
+ file://CVE-2025-27587-2.patch \
"
SRC_URI:append:class-nativesdk = " \
--
2.43.0
^ permalink raw reply related [flat|nested] 19+ messages in thread* [OE-core][scarthgap 08/16] libpam: fix CVE-2025-6020
2025-07-24 21:35 [OE-core][scarthgap 00/16] Patch review Steve Sakoman
` (6 preceding siblings ...)
2025-07-24 21:35 ` [OE-core][scarthgap 07/16] openssl: patch CVE-2025-27587 Steve Sakoman
@ 2025-07-24 21:35 ` Steve Sakoman
2025-07-24 21:35 ` [OE-core][scarthgap 09/16] glibc: stable 2.39 branch updates Steve Sakoman
` (7 subsequent siblings)
15 siblings, 0 replies; 19+ messages in thread
From: Steve Sakoman @ 2025-07-24 21:35 UTC (permalink / raw)
To: openembedded-core
From: Hitendra Prajapati <hprajapati@mvista.com>
Upstream-Status: Backport from https://github.com/linux-pam/linux-pam/commit/475bd60c552b98c7eddb3270b0b4196847c0072e && https://github.com/linux-pam/linux-pam/commit/592d84e1265d04c3104acee815a503856db503a1 && https://github.com/linux-pam/linux-pam/commit/976c20079358d133514568fc7fd95c02df8b5773
Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
Signed-off-by: Steve Sakoman <steve@sakoman.com>
---
.../libpam/0001-pam-inline-pam-asprintf.patch | 101 ++
.../libpam/0002-pam-namespace-rebase.patch | 750 +++++++++++
.../pam/libpam/CVE-2025-6020-01.patch | 1128 +++++++++++++++++
.../pam/libpam/CVE-2025-6020-02.patch | 187 +++
.../pam/libpam/CVE-2025-6020-03.patch | 35 +
meta/recipes-extended/pam/libpam_1.5.3.bb | 5 +
6 files changed, 2206 insertions(+)
create mode 100644 meta/recipes-extended/pam/libpam/0001-pam-inline-pam-asprintf.patch
create mode 100644 meta/recipes-extended/pam/libpam/0002-pam-namespace-rebase.patch
create mode 100644 meta/recipes-extended/pam/libpam/CVE-2025-6020-01.patch
create mode 100644 meta/recipes-extended/pam/libpam/CVE-2025-6020-02.patch
create mode 100644 meta/recipes-extended/pam/libpam/CVE-2025-6020-03.patch
diff --git a/meta/recipes-extended/pam/libpam/0001-pam-inline-pam-asprintf.patch b/meta/recipes-extended/pam/libpam/0001-pam-inline-pam-asprintf.patch
new file mode 100644
index 0000000000..9d1a0223df
--- /dev/null
+++ b/meta/recipes-extended/pam/libpam/0001-pam-inline-pam-asprintf.patch
@@ -0,0 +1,101 @@
+From 10b80543807e3fc5af5f8bcfd8bb6e219bb3cecc Mon Sep 17 00:00:00 2001
+From: "Dmitry V. Levin" <ldv@strace.io>
+Date: Tue, 18 Feb 2025 08:00:00 +0000
+Subject: [PATCH] pam_inline: introduce pam_asprintf(), pam_snprintf(), and
+ pam_sprintf()
+
+pam_asprintf() is essentially asprintf() with the following semantic
+difference: it returns the string itself instead of its length.
+
+pam_snprintf() is essentially snprintf() with the following semantic
+difference: it returns -1 in case of truncation.
+
+pam_sprintf() is essentially snprintf() but with a check that the buffer
+is an array, and with an automatically calculated buffer size.
+
+Use of these helpers would make error checking simpler.
+
+(cherry picked from commit 10b80543807e3fc5af5f8bcfd8bb6e219bb3cecc)
+Signed-off-by: Dmitry V. Levin <ldv@strace.io>
+
+Upstream-Status: Backport [https://github.com/linux-pam/linux-pam/commit/10b80543807e3fc5af5f8bcfd8bb6e219bb3cecc]
+Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
+---
+ libpam/include/pam_cc_compat.h | 6 ++++++
+ libpam/include/pam_inline.h | 36 ++++++++++++++++++++++++++++++++++
+ 2 files changed, 42 insertions(+)
+
+diff --git a/libpam/include/pam_cc_compat.h b/libpam/include/pam_cc_compat.h
+index 0a6e32d..af05428 100644
+--- a/libpam/include/pam_cc_compat.h
++++ b/libpam/include/pam_cc_compat.h
+@@ -21,6 +21,12 @@
+ # define PAM_ATTRIBUTE_ALIGNED(arg) /* empty */
+ #endif
+
++#if PAM_GNUC_PREREQ(3, 0)
++# define PAM_ATTRIBUTE_MALLOC __attribute__((__malloc__))
++#else
++# define PAM_ATTRIBUTE_MALLOC /* empty */
++#endif
++
+ #if PAM_GNUC_PREREQ(4, 6)
+ # define DIAG_PUSH_IGNORE_CAST_QUAL \
+ _Pragma("GCC diagnostic push"); \
+diff --git a/libpam/include/pam_inline.h b/libpam/include/pam_inline.h
+index 7721c0b..ec0497c 100644
+--- a/libpam/include/pam_inline.h
++++ b/libpam/include/pam_inline.h
+@@ -9,6 +9,8 @@
+ #define PAM_INLINE_H
+
+ #include "pam_cc_compat.h"
++#include <stdarg.h>
++#include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+ #include <unistd.h>
+@@ -126,6 +128,40 @@ pam_drop_response(struct pam_response *reply, int replies)
+ }
+
+
++static inline char * PAM_FORMAT((printf, 1, 2)) PAM_NONNULL((1)) PAM_ATTRIBUTE_MALLOC
++pam_asprintf(const char *fmt, ...)
++{
++ int rc;
++ char *res;
++ va_list ap;
++
++ va_start(ap, fmt);
++ rc = vasprintf(&res, fmt, ap);
++ va_end(ap);
++
++ return rc < 0 ? NULL : res;
++}
++
++static inline int PAM_FORMAT((printf, 3, 4)) PAM_NONNULL((3))
++pam_snprintf(char *str, size_t size, const char *fmt, ...)
++{
++ int rc;
++ va_list ap;
++
++ va_start(ap, fmt);
++ rc = vsnprintf(str, size, fmt, ap);
++ va_end(ap);
++
++ if (rc < 0 || (unsigned int) rc >= size)
++ return -1;
++ return rc;
++}
++
++#define pam_sprintf(str_, fmt_, ...) \
++ pam_snprintf((str_), sizeof(str_) + PAM_MUST_BE_ARRAY(str_), (fmt_), \
++ ##__VA_ARGS__)
++
++
+ static inline int
+ pam_read_passwords(int fd, int npass, char **passwords)
+ {
+--
+2.49.0
+
diff --git a/meta/recipes-extended/pam/libpam/0002-pam-namespace-rebase.patch b/meta/recipes-extended/pam/libpam/0002-pam-namespace-rebase.patch
new file mode 100644
index 0000000000..ff5a8a4946
--- /dev/null
+++ b/meta/recipes-extended/pam/libpam/0002-pam-namespace-rebase.patch
@@ -0,0 +1,750 @@
+From df1dab1a1a7900650ad4be157fea1a002048cc49 Mon Sep 17 00:00:00 2001
+From: Olivier Bal-Petre <olivier.bal-petre@ssi.gouv.fr>
+Date: Tue, 4 Mar 2025 14:37:02 +0100
+Subject: [PATCH ] pam-namespace-rebase
+
+Refresh the pam-namespace.
+
+Upstream-Status: Backport [https://github.com/linux-pam/linux-pam/commit/a8b4dce7b53d73de372e150028c970ee0a2a2e97]
+Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
+---
+ modules/pam_namespace/pam_namespace.c | 444 +++++++++++++-------------
+ modules/pam_namespace/pam_namespace.h | 7 +-
+ 2 files changed, 224 insertions(+), 227 deletions(-)
+
+diff --git a/modules/pam_namespace/pam_namespace.c b/modules/pam_namespace/pam_namespace.c
+index b026861..166bfce 100644
+--- a/modules/pam_namespace/pam_namespace.c
++++ b/modules/pam_namespace/pam_namespace.c
+@@ -41,7 +41,7 @@
+ #include "pam_namespace.h"
+ #include "argv_parse.h"
+
+-/* --- evaluting all files in VENDORDIR/security/namespace.d and /etc/security/namespace.d --- */
++/* --- evaluating all files in VENDORDIR/security/namespace.d and /etc/security/namespace.d --- */
+ static const char *base_name(const char *path)
+ {
+ const char *base = strrchr(path, '/');
+@@ -55,6 +55,155 @@ compare_filename(const void *a, const void *b)
+ base_name(* (char * const *) b));
+ }
+
++static void close_fds_pre_exec(struct instance_data *idata)
++{
++ if (pam_modutil_sanitize_helper_fds(idata->pamh, PAM_MODUTIL_IGNORE_FD,
++ PAM_MODUTIL_IGNORE_FD, PAM_MODUTIL_IGNORE_FD) < 0) {
++ _exit(1);
++ }
++}
++
++static void
++strip_trailing_slashes(char *str)
++{
++ char *p = str + strlen(str);
++
++ while (--p > str && *p == '/')
++ *p = '\0';
++}
++
++static int protect_mount(int dfd, const char *path, struct instance_data *idata)
++{
++ struct protect_dir_s *dir = idata->protect_dirs;
++ char tmpbuf[64];
++
++ while (dir != NULL) {
++ if (strcmp(path, dir->dir) == 0) {
++ return 0;
++ }
++ dir = dir->next;
++ }
++
++ if (pam_sprintf(tmpbuf, "/proc/self/fd/%d", dfd) < 0)
++ return -1;
++
++ dir = calloc(1, sizeof(*dir));
++
++ if (dir == NULL) {
++ return -1;
++ }
++
++ dir->dir = strdup(path);
++
++ if (dir->dir == NULL) {
++ free(dir);
++ return -1;
++ }
++
++ if (idata->flags & PAMNS_DEBUG) {
++ pam_syslog(idata->pamh, LOG_INFO,
++ "Protect mount of %s over itself", path);
++ }
++
++ if (mount(tmpbuf, tmpbuf, NULL, MS_BIND, NULL) != 0) {
++ int save_errno = errno;
++ pam_syslog(idata->pamh, LOG_ERR,
++ "Protect mount of %s failed: %m", tmpbuf);
++ free(dir->dir);
++ free(dir);
++ errno = save_errno;
++ return -1;
++ }
++
++ dir->next = idata->protect_dirs;
++ idata->protect_dirs = dir;
++
++ return 0;
++}
++
++static int protect_dir(const char *path, mode_t mode, int do_mkdir,
++ struct instance_data *idata)
++{
++ char *p = strdup(path);
++ char *d;
++ char *dir = p;
++ int dfd = AT_FDCWD;
++ int dfd_next;
++ int save_errno;
++ int flags = O_RDONLY | O_DIRECTORY;
++ int rv = -1;
++ struct stat st;
++
++ if (p == NULL) {
++ return -1;
++ }
++
++ if (*dir == '/') {
++ dfd = open("/", flags);
++ if (dfd == -1) {
++ goto error;
++ }
++ dir++; /* assume / is safe */
++ }
++
++ while ((d=strchr(dir, '/')) != NULL) {
++ *d = '\0';
++ dfd_next = openat(dfd, dir, flags);
++ if (dfd_next == -1) {
++ goto error;
++ }
++
++ if (dfd != AT_FDCWD)
++ close(dfd);
++ dfd = dfd_next;
++
++ if (fstat(dfd, &st) != 0) {
++ goto error;
++ }
++
++ if (flags & O_NOFOLLOW) {
++ /* we are inside user-owned dir - protect */
++ if (protect_mount(dfd, p, idata) == -1)
++ goto error;
++ } else if (st.st_uid != 0 || st.st_gid != 0 ||
++ (st.st_mode & S_IWOTH)) {
++ /* do not follow symlinks on subdirectories */
++ flags |= O_NOFOLLOW;
++ }
++
++ *d = '/';
++ dir = d + 1;
++ }
++
++ rv = openat(dfd, dir, flags);
++
++ if (rv == -1) {
++ if (!do_mkdir || mkdirat(dfd, dir, mode) != 0) {
++ goto error;
++ }
++ rv = openat(dfd, dir, flags);
++ }
++
++ if (flags & O_NOFOLLOW) {
++ /* we are inside user-owned dir - protect */
++ if (protect_mount(rv, p, idata) == -1) {
++ save_errno = errno;
++ close(rv);
++ rv = -1;
++ errno = save_errno;
++ }
++ }
++
++error:
++ save_errno = errno;
++ free(p);
++ if (dfd != AT_FDCWD && dfd >= 0)
++ close(dfd);
++ errno = save_errno;
++
++ return rv;
++}
++
+ /* Evaluating a list of files which have to be parsed in the right order:
+ *
+ * - If etc/security/namespace.d/@filename@.conf exists, then
+@@ -129,6 +278,7 @@ static char **read_namespace_dir(struct instance_data *idata)
+ return file_list;
+ }
+
++
+ /*
+ * Adds an entry for a polyinstantiated directory to the linked list of
+ * polyinstantiated directories. It is called from process_line() while
+@@ -198,7 +348,7 @@ static void cleanup_protect_data(pam_handle_t *pamh UNUSED , void *data, int err
+ unprotect_dirs(data);
+ }
+
+-static char *expand_variables(const char *orig, const char *var_names[], const char *var_values[])
++static char *expand_variables(const char *orig, const char *const var_names[], const char *var_values[])
+ {
+ const char *src = orig;
+ char *dst;
+@@ -209,7 +359,7 @@ static char *expand_variables(const char *orig, const char *var_names[], const c
+ if (*src == '$') {
+ int i;
+ for (i = 0; var_names[i]; i++) {
+- int namelen = strlen(var_names[i]);
++ size_t namelen = strlen(var_names[i]);
+ if (strncmp(var_names[i], src+1, namelen) == 0) {
+ dstlen += strlen(var_values[i]) - 1; /* $ */
+ src += namelen;
+@@ -227,7 +377,7 @@ static char *expand_variables(const char *orig, const char *var_names[], const c
+ if (c == '$') {
+ int i;
+ for (i = 0; var_names[i]; i++) {
+- int namelen = strlen(var_names[i]);
++ size_t namelen = strlen(var_names[i]);
+ if (strncmp(var_names[i], src+1, namelen) == 0) {
+ dst = stpcpy(dst, var_values[i]);
+ --dst;
+@@ -311,8 +461,7 @@ static int parse_iscript_params(char *params, struct polydir_s *poly)
+
+ if (*params != '\0') {
+ if (*params != '/') { /* path is relative to NAMESPACE_D_DIR */
+- if (asprintf(&poly->init_script, "%s%s", NAMESPACE_D_DIR, params) == -1)
+- return -1;
++ poly->init_script = pam_asprintf("%s%s", NAMESPACE_D_DIR, params);
+ } else {
+ poly->init_script = strdup(params);
+ }
+@@ -394,9 +543,9 @@ static int parse_method(char *method, struct polydir_s *poly,
+ {
+ enum polymethod pm;
+ char *sptr = NULL;
+- static const char *method_names[] = { "user", "context", "level", "tmpdir",
++ static const char *const method_names[] = { "user", "context", "level", "tmpdir",
+ "tmpfs", NULL };
+- static const char *flag_names[] = { "create", "noinit", "iscript",
++ static const char *const flag_names[] = { "create", "noinit", "iscript",
+ "shared", "mntopts", NULL };
+ static const unsigned int flag_values[] = { POLYDIR_CREATE, POLYDIR_NOINIT,
+ POLYDIR_ISCRIPT, POLYDIR_SHARED, POLYDIR_MNTOPTS };
+@@ -421,7 +570,7 @@ static int parse_method(char *method, struct polydir_s *poly,
+
+ while ((flag=strtok_r(NULL, ":", &sptr)) != NULL) {
+ for (i = 0; flag_names[i]; i++) {
+- int namelen = strlen(flag_names[i]);
++ size_t namelen = strlen(flag_names[i]);
+
+ if (strncmp(flag, flag_names[i], namelen) == 0) {
+ poly->flags |= flag_values[i];
+@@ -467,27 +616,27 @@ static int parse_method(char *method, struct polydir_s *poly,
+ * of the namespace configuration file. It skips over comments and incomplete
+ * or malformed lines. It processes a valid line with information on
+ * polyinstantiating a directory by populating appropriate fields of a
+- * polyinstatiated directory structure and then calling add_polydir_entry to
++ * polyinstantiated directory structure and then calling add_polydir_entry to
+ * add that entry to the linked list of polyinstantiated directories.
+ */
+ static int process_line(char *line, const char *home, const char *rhome,
+ struct instance_data *idata)
+ {
+ char *dir = NULL, *instance_prefix = NULL, *rdir = NULL;
++ const char *config_dir, *config_instance_prefix;
+ char *method, *uids;
+ char *tptr;
+ struct polydir_s *poly;
+ int retval = 0;
+ char **config_options = NULL;
+- static const char *var_names[] = {"HOME", "USER", NULL};
++ static const char *const var_names[] = {"HOME", "USER", NULL};
+ const char *var_values[] = {home, idata->user};
+ const char *rvar_values[] = {rhome, idata->ruser};
+- int len;
+
+ /*
+ * skip the leading white space
+ */
+- while (*line && isspace(*line))
++ while (*line && isspace((unsigned char)*line))
+ line++;
+
+ /*
+@@ -523,22 +672,19 @@ static int process_line(char *line, const char *home, const char *rhome,
+ goto erralloc;
+ }
+
+- dir = config_options[0];
+- if (dir == NULL) {
++ config_dir = config_options[0];
++ if (config_dir == NULL) {
+ pam_syslog(idata->pamh, LOG_NOTICE, "Invalid line missing polydir");
+ goto skipping;
+ }
+- instance_prefix = config_options[1];
+- if (instance_prefix == NULL) {
++ config_instance_prefix = config_options[1];
++ if (config_instance_prefix == NULL) {
+ pam_syslog(idata->pamh, LOG_NOTICE, "Invalid line missing instance_prefix");
+- instance_prefix = NULL;
+ goto skipping;
+ }
+ method = config_options[2];
+ if (method == NULL) {
+ pam_syslog(idata->pamh, LOG_NOTICE, "Invalid line missing method");
+- instance_prefix = NULL;
+- dir = NULL;
+ goto skipping;
+ }
+
+@@ -553,19 +699,16 @@ static int process_line(char *line, const char *home, const char *rhome,
+ /*
+ * Expand $HOME and $USER in poly dir and instance dir prefix
+ */
+- if ((rdir=expand_variables(dir, var_names, rvar_values)) == NULL) {
+- instance_prefix = NULL;
+- dir = NULL;
++ if ((rdir = expand_variables(config_dir, var_names, rvar_values)) == NULL) {
+ goto erralloc;
+ }
+
+- if ((dir=expand_variables(dir, var_names, var_values)) == NULL) {
+- instance_prefix = NULL;
++ if ((dir = expand_variables(config_dir, var_names, var_values)) == NULL) {
+ goto erralloc;
+ }
+
+- if ((instance_prefix=expand_variables(instance_prefix, var_names, var_values))
+- == NULL) {
++ if ((instance_prefix = expand_variables(config_instance_prefix,
++ var_names, var_values)) == NULL) {
+ goto erralloc;
+ }
+
+@@ -575,15 +718,8 @@ static int process_line(char *line, const char *home, const char *rhome,
+ pam_syslog(idata->pamh, LOG_DEBUG, "Expanded instance prefix: '%s'", instance_prefix);
+ }
+
+- len = strlen(dir);
+- if (len > 0 && dir[len-1] == '/') {
+- dir[len-1] = '\0';
+- }
+-
+- len = strlen(rdir);
+- if (len > 0 && rdir[len-1] == '/') {
+- rdir[len-1] = '\0';
+- }
++ strip_trailing_slashes(dir);
++ strip_trailing_slashes(rdir);
+
+ if (dir[0] == '\0' || rdir[0] == '\0') {
+ pam_syslog(idata->pamh, LOG_NOTICE, "Invalid polydir");
+@@ -594,26 +730,19 @@ static int process_line(char *line, const char *home, const char *rhome,
+ * Populate polyinstantiated directory structure with appropriate
+ * pathnames and the method with which to polyinstantiate.
+ */
+- if (strlen(dir) >= sizeof(poly->dir)
+- || strlen(rdir) >= sizeof(poly->rdir)
+- || strlen(instance_prefix) >= sizeof(poly->instance_prefix)) {
+- pam_syslog(idata->pamh, LOG_NOTICE, "Pathnames too long");
+- goto skipping;
+- }
+- strcpy(poly->dir, dir);
+- strcpy(poly->rdir, rdir);
+- strcpy(poly->instance_prefix, instance_prefix);
+-
+ if (parse_method(method, poly, idata) != 0) {
+ goto skipping;
+ }
+
+- if (poly->method == TMPDIR) {
+- if (sizeof(poly->instance_prefix) - strlen(poly->instance_prefix) < 7) {
+- pam_syslog(idata->pamh, LOG_NOTICE, "Pathnames too long");
+- goto skipping;
+- }
+- strcat(poly->instance_prefix, "XXXXXX");
++#define COPY_STR(dst, src, apd) \
++ pam_sprintf((dst), "%s%s", (src), (apd))
++
++ if (COPY_STR(poly->dir, dir, "") < 0
++ || COPY_STR(poly->rdir, rdir, "") < 0
++ || COPY_STR(poly->instance_prefix, instance_prefix,
++ poly->method == TMPDIR ? "XXXXXX" : "") < 0) {
++ pam_syslog(idata->pamh, LOG_NOTICE, "Pathnames too long");
++ goto skipping;
+ }
+
+ /*
+@@ -637,7 +766,7 @@ static int process_line(char *line, const char *home, const char *rhome,
+ if (uids) {
+ uid_t *uidptr;
+ const char *ustr, *sstr;
+- int count, i;
++ size_t count, i;
+
+ if (*uids == '~') {
+ poly->flags |= POLYDIR_EXCLUSIVE;
+@@ -646,8 +775,13 @@ static int process_line(char *line, const char *home, const char *rhome,
+ for (count = 0, ustr = sstr = uids; sstr; ustr = sstr + 1, count++)
+ sstr = strchr(ustr, ',');
+
++ if (count > UINT_MAX || count > SIZE_MAX / sizeof(uid_t)) {
++ pam_syslog(idata->pamh, LOG_ERR, "Too many uids encountered in configuration");
++ goto skipping;
++ }
++
+ poly->num_uids = count;
+- poly->uid = (uid_t *) malloc(count * sizeof (uid_t));
++ poly->uid = malloc(count * sizeof (uid_t));
+ uidptr = poly->uid;
+ if (uidptr == NULL) {
+ goto erralloc;
+@@ -996,6 +1130,7 @@ static int form_context(const struct polydir_s *polyptr,
+ return rc;
+ }
+ /* Should never get here */
++ freecon(scon);
+ return PAM_SUCCESS;
+ }
+ #endif
+@@ -1057,10 +1192,8 @@ static int poly_name(const struct polydir_s *polyptr, char **i_name,
+
+ switch (pm) {
+ case USER:
+- if (asprintf(i_name, "%s", idata->user) < 0) {
+- *i_name = NULL;
++ if ((*i_name = strdup(idata->user)) == NULL)
+ goto fail;
+- }
+ break;
+
+ #ifdef WITH_SELINUX
+@@ -1070,17 +1203,12 @@ static int poly_name(const struct polydir_s *polyptr, char **i_name,
+ pam_syslog(idata->pamh, LOG_ERR, "Error translating directory context");
+ goto fail;
+ }
+- if (polyptr->flags & POLYDIR_SHARED) {
+- if (asprintf(i_name, "%s", rawcon) < 0) {
+- *i_name = NULL;
+- goto fail;
+- }
+- } else {
+- if (asprintf(i_name, "%s_%s", rawcon, idata->user) < 0) {
+- *i_name = NULL;
+- goto fail;
+- }
+- }
++ if (polyptr->flags & POLYDIR_SHARED)
++ *i_name = strdup(rawcon);
++ else
++ *i_name = pam_asprintf("%s_%s", rawcon, idata->user);
++ if (*i_name == NULL)
++ goto fail;
+ break;
+
+ #endif /* WITH_SELINUX */
+@@ -1110,11 +1238,12 @@ static int poly_name(const struct polydir_s *polyptr, char **i_name,
+ *i_name = hash;
+ hash = NULL;
+ } else {
+- char *newname;
+- if (asprintf(&newname, "%.*s_%s", NAMESPACE_MAX_DIR_LEN-1-(int)strlen(hash),
+- *i_name, hash) < 0) {
++ char *newname =
++ pam_asprintf("%.*s_%s",
++ NAMESPACE_MAX_DIR_LEN - 1 - (int)strlen(hash),
++ *i_name, hash);
++ if (newname == NULL)
+ goto fail;
+- }
+ free(*i_name);
+ *i_name = newname;
+ }
+@@ -1139,137 +1268,6 @@ fail:
+ return rc;
+ }
+
+-static int protect_mount(int dfd, const char *path, struct instance_data *idata)
+-{
+- struct protect_dir_s *dir = idata->protect_dirs;
+- char tmpbuf[64];
+-
+- while (dir != NULL) {
+- if (strcmp(path, dir->dir) == 0) {
+- return 0;
+- }
+- dir = dir->next;
+- }
+-
+- dir = calloc(1, sizeof(*dir));
+-
+- if (dir == NULL) {
+- return -1;
+- }
+-
+- dir->dir = strdup(path);
+-
+- if (dir->dir == NULL) {
+- free(dir);
+- return -1;
+- }
+-
+- snprintf(tmpbuf, sizeof(tmpbuf), "/proc/self/fd/%d", dfd);
+-
+- if (idata->flags & PAMNS_DEBUG) {
+- pam_syslog(idata->pamh, LOG_INFO,
+- "Protect mount of %s over itself", path);
+- }
+-
+- if (mount(tmpbuf, tmpbuf, NULL, MS_BIND, NULL) != 0) {
+- int save_errno = errno;
+- pam_syslog(idata->pamh, LOG_ERR,
+- "Protect mount of %s failed: %m", tmpbuf);
+- free(dir->dir);
+- free(dir);
+- errno = save_errno;
+- return -1;
+- }
+-
+- dir->next = idata->protect_dirs;
+- idata->protect_dirs = dir;
+-
+- return 0;
+-}
+-
+-static int protect_dir(const char *path, mode_t mode, int do_mkdir,
+- struct instance_data *idata)
+-{
+- char *p = strdup(path);
+- char *d;
+- char *dir = p;
+- int dfd = AT_FDCWD;
+- int dfd_next;
+- int save_errno;
+- int flags = O_RDONLY | O_DIRECTORY;
+- int rv = -1;
+- struct stat st;
+-
+- if (p == NULL) {
+- goto error;
+- }
+-
+- if (*dir == '/') {
+- dfd = open("/", flags);
+- if (dfd == -1) {
+- goto error;
+- }
+- dir++; /* assume / is safe */
+- }
+-
+- while ((d=strchr(dir, '/')) != NULL) {
+- *d = '\0';
+- dfd_next = openat(dfd, dir, flags);
+- if (dfd_next == -1) {
+- goto error;
+- }
+-
+- if (dfd != AT_FDCWD)
+- close(dfd);
+- dfd = dfd_next;
+-
+- if (fstat(dfd, &st) != 0) {
+- goto error;
+- }
+-
+- if (flags & O_NOFOLLOW) {
+- /* we are inside user-owned dir - protect */
+- if (protect_mount(dfd, p, idata) == -1)
+- goto error;
+- } else if (st.st_uid != 0 || st.st_gid != 0 ||
+- (st.st_mode & S_IWOTH)) {
+- /* do not follow symlinks on subdirectories */
+- flags |= O_NOFOLLOW;
+- }
+-
+- *d = '/';
+- dir = d + 1;
+- }
+-
+- rv = openat(dfd, dir, flags);
+-
+- if (rv == -1) {
+- if (!do_mkdir || mkdirat(dfd, dir, mode) != 0) {
+- goto error;
+- }
+- rv = openat(dfd, dir, flags);
+- }
+-
+- if (flags & O_NOFOLLOW) {
+- /* we are inside user-owned dir - protect */
+- if (protect_mount(rv, p, idata) == -1) {
+- save_errno = errno;
+- close(rv);
+- rv = -1;
+- errno = save_errno;
+- }
+- }
+-
+-error:
+- save_errno = errno;
+- free(p);
+- if (dfd != AT_FDCWD && dfd >= 0)
+- close(dfd);
+- errno = save_errno;
+-
+- return rv;
+-}
+-
+ static int check_inst_parent(char *ipath, struct instance_data *idata)
+ {
+ struct stat instpbuf;
+@@ -1281,13 +1279,12 @@ static int check_inst_parent(char *ipath, struct instance_data *idata)
+ * admin explicitly instructs to ignore the instance parent
+ * mode by the "ignore_instance_parent_mode" argument).
+ */
+- inst_parent = (char *) malloc(strlen(ipath)+1);
++ inst_parent = strdup(ipath);
+ if (!inst_parent) {
+ pam_syslog(idata->pamh, LOG_CRIT, "Error allocating pathname string");
+ return PAM_SESSION_ERR;
+ }
+
+- strcpy(inst_parent, ipath);
+ trailing_slash = strrchr(inst_parent, '/');
+ if (trailing_slash)
+ *trailing_slash = '\0';
+@@ -1371,9 +1368,10 @@ static int inst_init(const struct polydir_s *polyptr, const char *ipath,
+ if (setuid(geteuid()) < 0) {
+ /* ignore failures, they don't matter */
+ }
++ close_fds_pre_exec(idata);
+
+- if (execle(init_script, init_script,
+- polyptr->dir, ipath, newdir?"1":"0", idata->user, NULL, envp) < 0)
++ execle(init_script, init_script,
++ polyptr->dir, ipath, newdir?"1":"0", idata->user, NULL, envp);
+ _exit(1);
+ } else if (pid > 0) {
+ while (((rc = waitpid(pid, &status, 0)) == (pid_t)-1) &&
+@@ -1424,7 +1422,9 @@ static int create_polydir(struct polydir_s *polyptr,
+
+ #ifdef WITH_SELINUX
+ if (idata->flags & PAMNS_SELINUX_ENABLED) {
+- getfscreatecon_raw(&oldcon_raw);
++ if (getfscreatecon_raw(&oldcon_raw) != 0)
++ pam_syslog(idata->pamh, LOG_NOTICE,
++ "Error retrieving fs create context: %m");
+
+ label_handle = selabel_open(SELABEL_CTX_FILE, NULL, 0);
+ if (!label_handle) {
+@@ -1453,6 +1453,9 @@ static int create_polydir(struct polydir_s *polyptr,
+ if (rc == -1) {
+ pam_syslog(idata->pamh, LOG_ERR,
+ "Error creating directory %s: %m", dir);
++#ifdef WITH_SELINUX
++ freecon(oldcon_raw);
++#endif
+ return PAM_SESSION_ERR;
+ }
+
+@@ -1640,16 +1643,14 @@ static int ns_setup(struct polydir_s *polyptr,
+
+ retval = protect_dir(polyptr->dir, 0, 0, idata);
+
+- if (retval < 0 && errno != ENOENT) {
+- pam_syslog(idata->pamh, LOG_ERR, "Polydir %s access error: %m",
+- polyptr->dir);
+- return PAM_SESSION_ERR;
+- }
+-
+ if (retval < 0) {
+- if ((polyptr->flags & POLYDIR_CREATE) &&
+- create_polydir(polyptr, idata) != PAM_SUCCESS)
+- return PAM_SESSION_ERR;
++ if (errno != ENOENT || !(polyptr->flags & POLYDIR_CREATE)) {
++ pam_syslog(idata->pamh, LOG_ERR, "Polydir %s access error: %m",
++ polyptr->dir);
++ return PAM_SESSION_ERR;
++ }
++ if (create_polydir(polyptr, idata) != PAM_SUCCESS)
++ return PAM_SESSION_ERR;
+ } else {
+ close(retval);
+ }
+@@ -1698,7 +1699,7 @@ static int ns_setup(struct polydir_s *polyptr,
+ #endif
+ }
+
+- if (asprintf(&inst_dir, "%s%s", polyptr->instance_prefix, instname) < 0)
++ if ((inst_dir = pam_asprintf("%s%s", polyptr->instance_prefix, instname)) == NULL)
+ goto error_out;
+
+ if (idata->flags & PAMNS_DEBUG)
+@@ -1810,8 +1811,9 @@ static int cleanup_tmpdirs(struct instance_data *idata)
+ _exit(1);
+ }
+ #endif
+- if (execle("/bin/rm", "/bin/rm", "-rf", pptr->instance_prefix, NULL, envp) < 0)
+- _exit(1);
++ close_fds_pre_exec(idata);
++ execle("/bin/rm", "/bin/rm", "-rf", pptr->instance_prefix, NULL, envp);
++ _exit(1);
+ } else if (pid > 0) {
+ while (((rc = waitpid(pid, &status, 0)) == (pid_t)-1) &&
+ (errno == EINTR));
+@@ -1826,7 +1828,7 @@ static int cleanup_tmpdirs(struct instance_data *idata)
+ }
+ } else if (pid < 0) {
+ pam_syslog(idata->pamh, LOG_ERR,
+- "Cannot fork to run namespace init script, %m");
++ "Cannot fork to cleanup temporary directory, %m");
+ rc = PAM_SESSION_ERR;
+ goto out;
+ }
+diff --git a/modules/pam_namespace/pam_namespace.h b/modules/pam_namespace/pam_namespace.h
+index a991b4c..180e042 100644
+--- a/modules/pam_namespace/pam_namespace.h
++++ b/modules/pam_namespace/pam_namespace.h
+@@ -44,21 +44,16 @@
+ #include <stdlib.h>
+ #include <errno.h>
+ #include <syslog.h>
+-#include <dlfcn.h>
+-#include <stdarg.h>
+ #include <pwd.h>
+ #include <grp.h>
+ #include <limits.h>
+ #include <sys/types.h>
+ #include <sys/stat.h>
+-#include <sys/resource.h>
+ #include <sys/mount.h>
+ #include <sys/wait.h>
+-#include <libgen.h>
+ #include <fcntl.h>
+ #include <sched.h>
+ #include <glob.h>
+-#include <locale.h>
+ #include "security/pam_modules.h"
+ #include "security/pam_modutil.h"
+ #include "security/pam_ext.h"
+@@ -114,7 +109,7 @@
+ #define PAMNS_MOUNT_PRIVATE 0x00080000 /* Make the polydir mounts private */
+
+ /* polydir flags */
+-#define POLYDIR_EXCLUSIVE 0x00000001 /* polyinstatiate exclusively for override uids */
++#define POLYDIR_EXCLUSIVE 0x00000001 /* polyinstantiate exclusively for override uids */
+ #define POLYDIR_CREATE 0x00000002 /* create the polydir */
+ #define POLYDIR_NOINIT 0x00000004 /* no init script */
+ #define POLYDIR_SHARED 0x00000008 /* share context/level instances among users */
+--
+2.49.0
+
diff --git a/meta/recipes-extended/pam/libpam/CVE-2025-6020-01.patch b/meta/recipes-extended/pam/libpam/CVE-2025-6020-01.patch
new file mode 100644
index 0000000000..ff0331aa38
--- /dev/null
+++ b/meta/recipes-extended/pam/libpam/CVE-2025-6020-01.patch
@@ -0,0 +1,1128 @@
+From 475bd60c552b98c7eddb3270b0b4196847c0072e Mon Sep 17 00:00:00 2001
+From: Olivier Bal-Petre <olivier.bal-petre@ssi.gouv.fr>
+Date: Tue, 4 Mar 2025 14:37:02 +0100
+Subject: [PATCH] pam_namespace: fix potential privilege escalation
+
+Existing protection provided by protect_dir() and protect_mount() were
+bind mounting on themselves all directories part of the to-be-secured
+paths. However, this works *only* against attacks executed by processes
+in the same mount namespace as the one the mountpoint was created in.
+Therefore, a user with an out-of-mount-namespace access, or multiple
+users colluding, could exploit multiple race conditions, and, for
+instance, elevate their privileges to root.
+
+This commit keeps the existing protection as a defense in depth
+measure, and to keep the existing behavior of the module. However,
+it converts all the needed function calls to operate on file
+descriptors instead of absolute paths to protect against race
+conditions globally.
+
+Signed-off-by: Olivier Bal-Petre <olivier.bal-petre@ssi.gouv.fr>
+Signed-off-by: Dmitry V. Levin <ldv@strace.io>
+
+Upstream-Status: Backport [https://github.com/linux-pam/linux-pam/commit/475bd60c552b98c7eddb3270b0b4196847c0072e]
+CVE: CVE-2025-6020
+Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
+---
+ modules/pam_namespace/pam_namespace.c | 637 ++++++++++++++++++--------
+ modules/pam_namespace/pam_namespace.h | 10 +
+ 2 files changed, 457 insertions(+), 190 deletions(-)
+
+diff --git a/modules/pam_namespace/pam_namespace.c b/modules/pam_namespace/pam_namespace.c
+index 166bfce..9d993d4 100644
+--- a/modules/pam_namespace/pam_namespace.c
++++ b/modules/pam_namespace/pam_namespace.c
+@@ -41,6 +41,8 @@
+ #include "pam_namespace.h"
+ #include "argv_parse.h"
+
++#define MAGIC_LNK_FD_SIZE 64
++
+ /* --- evaluating all files in VENDORDIR/security/namespace.d and /etc/security/namespace.d --- */
+ static const char *base_name(const char *path)
+ {
+@@ -75,7 +77,7 @@ strip_trailing_slashes(char *str)
+ static int protect_mount(int dfd, const char *path, struct instance_data *idata)
+ {
+ struct protect_dir_s *dir = idata->protect_dirs;
+- char tmpbuf[64];
++ char tmpbuf[MAGIC_LNK_FD_SIZE];
+
+ while (dir != NULL) {
+ if (strcmp(path, dir->dir) == 0) {
+@@ -121,56 +123,107 @@ static int protect_mount(int dfd, const char *path, struct instance_data *idata)
+ return 0;
+ }
+
+-static int protect_dir(const char *path, mode_t mode, int do_mkdir,
++/*
++ * Returns a fd to the given absolute path, acquired securely. This means:
++ * - iterating on each segment of the path,
++ * - not following user symlinks,
++ * - using race-free operations.
++ *
++ * Takes a bit mask to specify the operation mode:
++ * - SECURE_OPENDIR_PROTECT: call protect_mount() on each unsafe segment of path
++ * - SECURE_OPENDIR_MKDIR: create last segment of path if does not exist
++ * - SECURE_OPENDIR_FULL_FD: open the directory with O_RDONLY instead of O_PATH,
++ * allowing more operations to be done with the returned fd
++ *
++ * Be aware that using SECURE_OPENDIR_PROTECT:
++ * - will modify some external state (global structure...) and should not be
++ * called in cleanup code paths. See wrapper secure_opendir_stateless()
++ * - need a non-NULL idata to call protect_mount()
++ */
++static int secure_opendir(const char *path, int opm, mode_t mode,
+ struct instance_data *idata)
+ {
+- char *p = strdup(path);
++ char *p;
+ char *d;
+- char *dir = p;
+- int dfd = AT_FDCWD;
++ char *dir;
++ int dfd = -1;
+ int dfd_next;
+ int save_errno;
+- int flags = O_RDONLY | O_DIRECTORY;
++ int flags = O_DIRECTORY | O_CLOEXEC;
+ int rv = -1;
+ struct stat st;
+
+- if (p == NULL) {
++ if (opm & SECURE_OPENDIR_FULL_FD)
++ flags |= O_RDONLY;
++ else
++ flags |= O_PATH;
++
++ /* Check for args consistency */
++ if ((opm & SECURE_OPENDIR_PROTECT) && idata == NULL)
+ return -1;
+- }
+
+- if (*dir == '/') {
+- dfd = open("/", flags);
+- if (dfd == -1) {
+- goto error;
+- }
+- dir++; /* assume / is safe */
++ /* Accept only absolute paths */
++ if (*path != '/')
++ return -1;
++
++ dir = p = strdup(path);
++ if (p == NULL)
++ return -1;
++
++ /* Assume '/' is safe */
++ dfd = open("/", flags);
++ if (dfd == -1)
++ goto error;
++
++ /* Needed to not loop too far and call openat() on NULL */
++ strip_trailing_slashes(p);
++
++ dir++;
++
++ /* In case path is '/' */
++ if (*dir == '\0') {
++ free(p);
++ return dfd;
+ }
+
+ while ((d=strchr(dir, '/')) != NULL) {
+ *d = '\0';
++
+ dfd_next = openat(dfd, dir, flags);
+- if (dfd_next == -1) {
++ if (dfd_next == -1)
+ goto error;
+- }
+-
+- if (dfd != AT_FDCWD)
+- close(dfd);
+- dfd = dfd_next;
+
+- if (fstat(dfd, &st) != 0) {
++ if (fstat(dfd_next, &st) != 0) {
++ close(dfd_next);
+ goto error;
+ }
+
+- if (flags & O_NOFOLLOW) {
++ if ((flags & O_NOFOLLOW) && (opm & SECURE_OPENDIR_PROTECT)) {
+ /* we are inside user-owned dir - protect */
+- if (protect_mount(dfd, p, idata) == -1)
++ if (protect_mount(dfd_next, p, idata) == -1) {
++ close(dfd_next);
++ goto error;
++ }
++ /*
++ * Reopen the directory to obtain a new descriptor
++ * after protect_mount(), this is necessary in cases
++ * when another directory is going to be mounted over
++ * the given path.
++ */
++ close(dfd_next);
++ dfd_next = openat(dfd, dir, flags);
++ if (dfd_next == -1)
+ goto error;
+- } else if (st.st_uid != 0 || st.st_gid != 0 ||
+- (st.st_mode & S_IWOTH)) {
++ } else if (st.st_uid != 0
++ || (st.st_gid != 0 && (st.st_mode & S_IWGRP))
++ || (st.st_mode & S_IWOTH)) {
+ /* do not follow symlinks on subdirectories */
+ flags |= O_NOFOLLOW;
+ }
+
++ close(dfd);
++ dfd = dfd_next;
++
+ *d = '/';
+ dir = d + 1;
+ }
+@@ -178,13 +231,14 @@ static int protect_dir(const char *path, mode_t mode, int do_mkdir,
+ rv = openat(dfd, dir, flags);
+
+ if (rv == -1) {
+- if (!do_mkdir || mkdirat(dfd, dir, mode) != 0) {
++ if ((opm & SECURE_OPENDIR_MKDIR) && mkdirat(dfd, dir, mode) == 0)
++ rv = openat(dfd, dir, flags);
++
++ if (rv == -1)
+ goto error;
+- }
+- rv = openat(dfd, dir, flags);
+ }
+
+- if (flags & O_NOFOLLOW) {
++ if ((flags & O_NOFOLLOW) && (opm & SECURE_OPENDIR_PROTECT)) {
+ /* we are inside user-owned dir - protect */
+ if (protect_mount(rv, p, idata) == -1) {
+ save_errno = errno;
+@@ -192,18 +246,95 @@ static int protect_dir(const char *path, mode_t mode, int do_mkdir,
+ rv = -1;
+ errno = save_errno;
+ }
++ /*
++ * Reopen the directory to obtain a new descriptor after
++ * protect_mount(), this is necessary in cases when another
++ * directory is going to be mounted over the given path.
++ */
++ close(rv);
++ rv = openat(dfd, dir, flags);
+ }
+
+ error:
+ save_errno = errno;
+ free(p);
+- if (dfd != AT_FDCWD && dfd >= 0)
++ if (dfd >= 0)
+ close(dfd);
+ errno = save_errno;
+
+ return rv;
+ }
+
++/*
++ * Returns a fd to the given path, acquired securely.
++ * It can be called in all situations, including in cleanup code paths, as
++ * it does not modify external state (no access to global structures...).
++ */
++static int secure_opendir_stateless(const char *path)
++{
++ return secure_opendir(path, 0, 0, NULL);
++}
++
++/*
++ * Umount securely the given path, even if the directories along
++ * the path are under user control. It should protect against
++ * symlinks attacks and race conditions.
++ */
++static int secure_umount(const char *path)
++{
++ int save_errno;
++ int rv = -1;
++ int dfd = -1;
++ char s_path[MAGIC_LNK_FD_SIZE];
++
++ dfd = secure_opendir_stateless(path);
++ if (dfd == -1)
++ return rv;
++
++ if (pam_sprintf(s_path, "/proc/self/fd/%d", dfd) < 0)
++ goto error;
++
++ /*
++ * We still have a fd open to path itself,
++ * so we need to do a lazy umount.
++ */
++ rv = umount2(s_path, MNT_DETACH);
++
++error:
++ save_errno = errno;
++ close(dfd);
++ errno = save_errno;
++ return rv;
++}
++
++/*
++ * Rmdir the given path securely, protecting against symlinks attacks
++ * and race conditions.
++ * This function is currently called only in cleanup code paths where
++ * any errors returned are not handled, so do not handle them either.
++ * Basically, try to rmdir the path on a best-effort basis.
++ */
++static void secure_try_rmdir(const char *path)
++{
++ int dfd;
++ char *buf;
++ char *parent;
++
++ buf = strdup(path);
++ if (buf == NULL)
++ return;
++
++ parent = dirname(buf);
++
++ dfd = secure_opendir_stateless(parent);
++ if (dfd >= 0) {
++ unlinkat(dfd, base_name(path), AT_REMOVEDIR);
++ close(dfd);
++ }
++
++ free(buf);
++}
++
+ /* Evaluating a list of files which have to be parsed in the right order:
+ *
+ * - If etc/security/namespace.d/@filename@.conf exists, then
+@@ -330,7 +461,7 @@ static void unprotect_dirs(struct protect_dir_s *dir)
+ struct protect_dir_s *next;
+
+ while (dir != NULL) {
+- umount(dir->dir);
++ secure_umount(dir->dir);
+ free(dir->dir);
+ next = dir->next;
+ free(dir);
+@@ -734,13 +865,9 @@ static int process_line(char *line, const char *home, const char *rhome,
+ goto skipping;
+ }
+
+-#define COPY_STR(dst, src, apd) \
+- pam_sprintf((dst), "%s%s", (src), (apd))
+-
+- if (COPY_STR(poly->dir, dir, "") < 0
+- || COPY_STR(poly->rdir, rdir, "") < 0
+- || COPY_STR(poly->instance_prefix, instance_prefix,
+- poly->method == TMPDIR ? "XXXXXX" : "") < 0) {
++ if (pam_sprintf(poly->dir, "%s", dir) < 0
++ || pam_sprintf(poly->rdir, "%s", rdir) < 0
++ || pam_sprintf(poly->instance_prefix, "%s", instance_prefix) < 0) {
+ pam_syslog(idata->pamh, LOG_NOTICE, "Pathnames too long");
+ goto skipping;
+ }
+@@ -1023,6 +1150,23 @@ static char *md5hash(const char *instname, struct instance_data *idata)
+ }
+
+ #ifdef WITH_SELINUX
++static char *secure_getfilecon(pam_handle_t *pamh, const char *dir)
++{
++ char *ctx = NULL;
++ int dfd = secure_opendir(dir, SECURE_OPENDIR_FULL_FD, 0, NULL);
++ if (dfd < 0) {
++ pam_syslog(pamh, LOG_ERR, "Error getting fd to %s: %m", dir);
++ return NULL;
++ }
++ if (fgetfilecon(dfd, &ctx) < 0)
++ ctx = NULL;
++ if (ctx == NULL)
++ pam_syslog(pamh, LOG_ERR,
++ "Error getting poly dir context for %s: %m", dir);
++ close(dfd);
++ return ctx;
++}
++
+ static int form_context(const struct polydir_s *polyptr,
+ char **i_context, char **origcon,
+ struct instance_data *idata)
+@@ -1034,12 +1178,9 @@ static int form_context(const struct polydir_s *polyptr,
+ /*
+ * Get the security context of the directory to polyinstantiate.
+ */
+- rc = getfilecon(polyptr->dir, origcon);
+- if (rc < 0 || *origcon == NULL) {
+- pam_syslog(idata->pamh, LOG_ERR,
+- "Error getting poly dir context, %m");
++ *origcon = secure_getfilecon(idata->pamh, polyptr->dir);
++ if (*origcon == NULL)
+ return PAM_SESSION_ERR;
+- }
+
+ if (polyptr->method == USER) return PAM_SUCCESS;
+
+@@ -1136,29 +1277,52 @@ static int form_context(const struct polydir_s *polyptr,
+ #endif
+
+ /*
+- * poly_name returns the name of the polyinstantiated instance directory
++ * From the instance differentiation string, set in the polyptr structure:
++ * - the absolute path to the instance dir,
++ * - the absolute path to the previous dir (parent),
++ * - the instance name (may be different than the instance differentiation string)
++ */
++static int set_polydir_paths(struct polydir_s *polyptr, const char *inst_differentiation)
++{
++ char *tmp;
++
++ if (pam_sprintf(polyptr->instance_absolute, "%s%s",
++ polyptr->instance_prefix, inst_differentiation) < 0)
++ return -1;
++
++ polyptr->instname = strrchr(polyptr->instance_absolute, '/') + 1;
++
++ if (pam_sprintf(polyptr->instance_parent, "%s", polyptr->instance_absolute) < 0)
++ return -1;
++
++ tmp = strrchr(polyptr->instance_parent, '/') + 1;
++ *tmp = '\0';
++
++ return 0;
++}
++
++/*
++ * Set the name of the polyinstantiated instance directory
+ * based on the method used for polyinstantiation (user, context or level)
+ * In addition, the function also returns the security contexts of the
+ * original directory to polyinstantiate and the polyinstantiated instance
+ * directory.
+ */
+ #ifdef WITH_SELINUX
+-static int poly_name(const struct polydir_s *polyptr, char **i_name,
+- char **i_context, char **origcon,
+- struct instance_data *idata)
++static int poly_name(struct polydir_s *polyptr, char **i_context,
++ char **origcon, struct instance_data *idata)
+ #else
+-static int poly_name(const struct polydir_s *polyptr, char **i_name,
+- struct instance_data *idata)
++static int poly_name(struct polydir_s *polyptr, struct instance_data *idata)
+ #endif
+ {
+ int rc;
++ char *inst_differentiation = NULL;
+ char *hash = NULL;
+ enum polymethod pm;
+ #ifdef WITH_SELINUX
+ char *rawcon = NULL;
+ #endif
+
+- *i_name = NULL;
+ #ifdef WITH_SELINUX
+ *i_context = NULL;
+ *origcon = NULL;
+@@ -1192,7 +1356,7 @@ static int poly_name(const struct polydir_s *polyptr, char **i_name,
+
+ switch (pm) {
+ case USER:
+- if ((*i_name = strdup(idata->user)) == NULL)
++ if ((inst_differentiation = strdup(idata->user)) == NULL)
+ goto fail;
+ break;
+
+@@ -1204,20 +1368,24 @@ static int poly_name(const struct polydir_s *polyptr, char **i_name,
+ goto fail;
+ }
+ if (polyptr->flags & POLYDIR_SHARED)
+- *i_name = strdup(rawcon);
++ inst_differentiation = strdup(rawcon);
+ else
+- *i_name = pam_asprintf("%s_%s", rawcon, idata->user);
+- if (*i_name == NULL)
++ inst_differentiation = pam_asprintf("%s_%s", rawcon, idata->user);
++ if (inst_differentiation == NULL)
+ goto fail;
+ break;
+
+ #endif /* WITH_SELINUX */
+
+ case TMPDIR:
++ if ((inst_differentiation = strdup("XXXXXX")) == NULL)
++ goto fail;
++ goto success;
++
+ case TMPFS:
+- if ((*i_name=strdup("")) == NULL)
++ if ((inst_differentiation=strdup("")) == NULL)
+ goto fail;
+- return PAM_SUCCESS;
++ goto success;
+
+ default:
+ if (idata->flags & PAMNS_DEBUG)
+@@ -1226,32 +1394,37 @@ static int poly_name(const struct polydir_s *polyptr, char **i_name,
+ }
+
+ if (idata->flags & PAMNS_DEBUG)
+- pam_syslog(idata->pamh, LOG_DEBUG, "poly_name %s", *i_name);
++ pam_syslog(idata->pamh, LOG_DEBUG, "poly_name %s", inst_differentiation);
+
+- if ((idata->flags & PAMNS_GEN_HASH) || strlen(*i_name) > NAMESPACE_MAX_DIR_LEN) {
+- hash = md5hash(*i_name, idata);
++ if ((idata->flags & PAMNS_GEN_HASH) || strlen(inst_differentiation) > NAMESPACE_MAX_DIR_LEN) {
++ hash = md5hash(inst_differentiation, idata);
+ if (hash == NULL) {
+ goto fail;
+ }
+ if (idata->flags & PAMNS_GEN_HASH) {
+- free(*i_name);
+- *i_name = hash;
++ free(inst_differentiation);
++ inst_differentiation = hash;
+ hash = NULL;
+ } else {
+ char *newname =
+ pam_asprintf("%.*s_%s",
+ NAMESPACE_MAX_DIR_LEN - 1 - (int)strlen(hash),
+- *i_name, hash);
++ inst_differentiation, hash);
+ if (newname == NULL)
+ goto fail;
+- free(*i_name);
+- *i_name = newname;
++ free(inst_differentiation);
++ inst_differentiation = newname;
+ }
+ }
+- rc = PAM_SUCCESS;
+
++success:
++ if (set_polydir_paths(polyptr, inst_differentiation) == -1)
++ goto fail;
++
++ rc = PAM_SUCCESS;
+ fail:
+ free(hash);
++ free(inst_differentiation);
+ #ifdef WITH_SELINUX
+ freecon(rawcon);
+ #endif
+@@ -1262,55 +1435,35 @@ fail:
+ freecon(*origcon);
+ *origcon = NULL;
+ #endif
+- free(*i_name);
+- *i_name = NULL;
+ }
+ return rc;
+ }
+
+-static int check_inst_parent(char *ipath, struct instance_data *idata)
++static int check_inst_parent(int dfd, struct instance_data *idata)
+ {
+ struct stat instpbuf;
+- char *inst_parent, *trailing_slash;
+- int dfd;
++
+ /*
+- * stat the instance parent path to make sure it exists
+- * and is a directory. Check that its mode is 000 (unless the
+- * admin explicitly instructs to ignore the instance parent
+- * mode by the "ignore_instance_parent_mode" argument).
++ * Stat the instance parent directory to make sure it's writable by
++ * root only (unless the admin explicitly instructs to ignore the
++ * instance parent mode by the "ignore_instance_parent_mode" argument).
+ */
+- inst_parent = strdup(ipath);
+- if (!inst_parent) {
+- pam_syslog(idata->pamh, LOG_CRIT, "Error allocating pathname string");
+- return PAM_SESSION_ERR;
+- }
+
+- trailing_slash = strrchr(inst_parent, '/');
+- if (trailing_slash)
+- *trailing_slash = '\0';
+-
+- dfd = protect_dir(inst_parent, 0, 1, idata);
++ if (idata->flags & PAMNS_IGN_INST_PARENT_MODE)
++ return PAM_SUCCESS;
+
+- if (dfd == -1 || fstat(dfd, &instpbuf) < 0) {
++ if (fstat(dfd, &instpbuf) < 0) {
+ pam_syslog(idata->pamh, LOG_ERR,
+- "Error creating or accessing instance parent %s, %m", inst_parent);
+- if (dfd != -1)
+- close(dfd);
+- free(inst_parent);
++ "Error accessing instance parent, %m");
+ return PAM_SESSION_ERR;
+ }
+
+- if ((idata->flags & PAMNS_IGN_INST_PARENT_MODE) == 0) {
+- if ((instpbuf.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) || instpbuf.st_uid != 0) {
+- pam_syslog(idata->pamh, LOG_ERR, "Mode of inst parent %s not 000 or owner not root",
+- inst_parent);
+- close(dfd);
+- free(inst_parent);
+- return PAM_SESSION_ERR;
+- }
++ if ((instpbuf.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) || instpbuf.st_uid != 0) {
++ pam_syslog(idata->pamh, LOG_ERR,
++ "Mode of inst parent not 000 or owner not root");
++ return PAM_SESSION_ERR;
+ }
+- close(dfd);
+- free(inst_parent);
++
+ return PAM_SUCCESS;
+ }
+
+@@ -1449,14 +1602,16 @@ static int create_polydir(struct polydir_s *polyptr,
+ }
+ #endif
+
+- rc = protect_dir(dir, mode, 1, idata);
++ rc = secure_opendir(dir,
++ SECURE_OPENDIR_PROTECT | SECURE_OPENDIR_MKDIR | SECURE_OPENDIR_FULL_FD,
++ mode, idata);
+ if (rc == -1) {
+ pam_syslog(idata->pamh, LOG_ERR,
+ "Error creating directory %s: %m", dir);
+ #ifdef WITH_SELINUX
+ freecon(oldcon_raw);
+ #endif
+- return PAM_SESSION_ERR;
++ return -1;
+ }
+
+ #ifdef WITH_SELINUX
+@@ -1477,9 +1632,9 @@ static int create_polydir(struct polydir_s *polyptr,
+ pam_syslog(idata->pamh, LOG_ERR,
+ "Error changing mode of directory %s: %m", dir);
+ close(rc);
+- umount(dir); /* undo the eventual protection bind mount */
+- rmdir(dir);
+- return PAM_SESSION_ERR;
++ secure_umount(dir); /* undo the eventual protection bind mount */
++ secure_try_rmdir(dir);
++ return -1;
+ }
+ }
+
+@@ -1497,41 +1652,37 @@ static int create_polydir(struct polydir_s *polyptr,
+ pam_syslog(idata->pamh, LOG_ERR,
+ "Unable to change owner on directory %s: %m", dir);
+ close(rc);
+- umount(dir); /* undo the eventual protection bind mount */
+- rmdir(dir);
+- return PAM_SESSION_ERR;
++ secure_umount(dir); /* undo the eventual protection bind mount */
++ secure_try_rmdir(dir);
++ return -1;
+ }
+
+- close(rc);
+-
+ if (idata->flags & PAMNS_DEBUG)
+ pam_syslog(idata->pamh, LOG_DEBUG,
+ "Polydir owner %u group %u", uid, gid);
+
+- return PAM_SUCCESS;
++ return rc;
+ }
+
+ /*
+- * Create polyinstantiated instance directory (ipath).
++ * Create polyinstantiated instance directory.
++ * To protect against races, changes are done on a fd to the parent of the
++ * instance directory (dfd_iparent) and a relative path (polyptr->instname).
++ * The absolute path (polyptr->instance_absolute) is only updated when creating
++ * a tmpdir and used for logging purposes.
+ */
+ #ifdef WITH_SELINUX
+-static int create_instance(struct polydir_s *polyptr, char *ipath, struct stat *statbuf,
+- const char *icontext, const char *ocontext,
+- struct instance_data *idata)
++static int create_instance(struct polydir_s *polyptr, int dfd_iparent,
++ struct stat *statbuf, const char *icontext, const char *ocontext,
++ struct instance_data *idata)
+ #else
+-static int create_instance(struct polydir_s *polyptr, char *ipath, struct stat *statbuf,
+- struct instance_data *idata)
++static int create_instance(struct polydir_s *polyptr, int dfd_iparent,
++ struct stat *statbuf, struct instance_data *idata)
+ #endif
+ {
+ struct stat newstatbuf;
+ int fd;
+
+- /*
+- * Check to make sure instance parent is valid.
+- */
+- if (check_inst_parent(ipath, idata))
+- return PAM_SESSION_ERR;
+-
+ /*
+ * Create instance directory and set its security context to the context
+ * returned by the security policy. Set its mode and ownership
+@@ -1540,29 +1691,39 @@ static int create_instance(struct polydir_s *polyptr, char *ipath, struct stat *
+ */
+
+ if (polyptr->method == TMPDIR) {
+- if (mkdtemp(polyptr->instance_prefix) == NULL) {
+- pam_syslog(idata->pamh, LOG_ERR, "Error creating temporary instance %s, %m",
+- polyptr->instance_prefix);
+- polyptr->method = NONE; /* do not clean up! */
+- return PAM_SESSION_ERR;
+- }
+- /* copy the actual directory name to ipath */
+- strcpy(ipath, polyptr->instance_prefix);
+- } else if (mkdir(ipath, S_IRUSR) < 0) {
++ char s_path[PATH_MAX];
++ /*
++ * Create the template for mkdtemp() as a magic link based on
++ * our existing fd to avoid symlink attacks and races.
++ */
++ if (pam_sprintf(s_path, "/proc/self/fd/%d/%s", dfd_iparent, polyptr->instname) < 0
++ || mkdtemp(s_path) == NULL) {
++ pam_syslog(idata->pamh, LOG_ERR,
++ "Error creating temporary instance dir %s, %m",
++ polyptr->instance_absolute);
++ polyptr->method = NONE; /* do not clean up! */
++ return PAM_SESSION_ERR;
++ }
++
++ /* Copy the actual directory name to polyptr->instname */
++ strcpy(polyptr->instname, base_name(s_path));
++ } else if (mkdirat(dfd_iparent, polyptr->instname, S_IRUSR) < 0) {
+ if (errno == EEXIST)
+ return PAM_IGNORE;
+ else {
+ pam_syslog(idata->pamh, LOG_ERR, "Error creating %s, %m",
+- ipath);
++ polyptr->instance_absolute);
+ return PAM_SESSION_ERR;
+ }
+ }
+
+- /* Open a descriptor to it to prevent races */
+- fd = open(ipath, O_DIRECTORY | O_RDONLY);
++ /* Open a descriptor to prevent races, based on our existing fd. */
++ fd = openat(dfd_iparent, polyptr->instname,
++ O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
+ if (fd < 0) {
+- pam_syslog(idata->pamh, LOG_ERR, "Error opening %s, %m", ipath);
+- rmdir(ipath);
++ pam_syslog(idata->pamh, LOG_ERR, "Error opening %s, %m",
++ polyptr->instance_absolute);
++ unlinkat(dfd_iparent, polyptr->instname, AT_REMOVEDIR);
+ return PAM_SESSION_ERR;
+ }
+ #ifdef WITH_SELINUX
+@@ -1572,17 +1733,19 @@ static int create_instance(struct polydir_s *polyptr, char *ipath, struct stat *
+ if (icontext) {
+ if (fsetfilecon(fd, icontext) < 0) {
+ pam_syslog(idata->pamh, LOG_ERR,
+- "Error setting context of %s to %s", ipath, icontext);
++ "Error setting context of %s to %s",
++ polyptr->instance_absolute, icontext);
+ close(fd);
+- rmdir(ipath);
++ unlinkat(dfd_iparent, polyptr->instname, AT_REMOVEDIR);
+ return PAM_SESSION_ERR;
+ }
+ } else {
+ if (fsetfilecon(fd, ocontext) < 0) {
+ pam_syslog(idata->pamh, LOG_ERR,
+- "Error setting context of %s to %s", ipath, ocontext);
++ "Error setting context of %s to %s",
++ polyptr->instance_absolute, ocontext);
+ close(fd);
+- rmdir(ipath);
++ unlinkat(dfd_iparent, polyptr->instname, AT_REMOVEDIR);
+ return PAM_SESSION_ERR;
+ }
+ }
+@@ -1590,9 +1753,9 @@ static int create_instance(struct polydir_s *polyptr, char *ipath, struct stat *
+ #endif
+ if (fstat(fd, &newstatbuf) < 0) {
+ pam_syslog(idata->pamh, LOG_ERR, "Error stating %s, %m",
+- ipath);
++ polyptr->instance_absolute);
+ close(fd);
+- rmdir(ipath);
++ unlinkat(dfd_iparent, polyptr->instname, AT_REMOVEDIR);
+ return PAM_SESSION_ERR;
+ }
+ if (newstatbuf.st_uid != statbuf->st_uid ||
+@@ -1600,17 +1763,17 @@ static int create_instance(struct polydir_s *polyptr, char *ipath, struct stat *
+ if (fchown(fd, statbuf->st_uid, statbuf->st_gid) < 0) {
+ pam_syslog(idata->pamh, LOG_ERR,
+ "Error changing owner for %s, %m",
+- ipath);
++ polyptr->instance_absolute);
+ close(fd);
+- rmdir(ipath);
++ unlinkat(dfd_iparent, polyptr->instname, AT_REMOVEDIR);
+ return PAM_SESSION_ERR;
+ }
+ }
+ if (fchmod(fd, statbuf->st_mode & 07777) < 0) {
+ pam_syslog(idata->pamh, LOG_ERR, "Error changing mode for %s, %m",
+- ipath);
++ polyptr->instance_absolute);
+ close(fd);
+- rmdir(ipath);
++ unlinkat(dfd_iparent, polyptr->instname, AT_REMOVEDIR);
+ return PAM_SESSION_ERR;
+ }
+ close(fd);
+@@ -1629,9 +1792,12 @@ static int ns_setup(struct polydir_s *polyptr,
+ struct instance_data *idata)
+ {
+ int retval;
++ int dfd_iparent = -1;
++ int dfd_ipath = -1;
++ int dfd_pptrdir = -1;
+ int newdir = 1;
+- char *inst_dir = NULL;
+- char *instname = NULL;
++ char s_ipath[MAGIC_LNK_FD_SIZE];
++ char s_pptrdir[MAGIC_LNK_FD_SIZE];
+ struct stat statbuf;
+ #ifdef WITH_SELINUX
+ char *instcontext = NULL, *origcontext = NULL;
+@@ -1641,37 +1807,48 @@ static int ns_setup(struct polydir_s *polyptr,
+ pam_syslog(idata->pamh, LOG_DEBUG,
+ "Set namespace for directory %s", polyptr->dir);
+
+- retval = protect_dir(polyptr->dir, 0, 0, idata);
++ dfd_pptrdir = secure_opendir(polyptr->dir, SECURE_OPENDIR_PROTECT, 0, idata);
+
+- if (retval < 0) {
++ if (dfd_pptrdir < 0) {
+ if (errno != ENOENT || !(polyptr->flags & POLYDIR_CREATE)) {
+ pam_syslog(idata->pamh, LOG_ERR, "Polydir %s access error: %m",
+ polyptr->dir);
+ return PAM_SESSION_ERR;
+ }
+- if (create_polydir(polyptr, idata) != PAM_SUCCESS)
++ dfd_pptrdir = create_polydir(polyptr, idata);
++ if (dfd_pptrdir < 0)
+ return PAM_SESSION_ERR;
+- } else {
+- close(retval);
+ }
+
+ if (polyptr->method == TMPFS) {
+- if (mount("tmpfs", polyptr->dir, "tmpfs", polyptr->mount_flags, polyptr->mount_opts) < 0) {
+- pam_syslog(idata->pamh, LOG_ERR, "Error mounting tmpfs on %s, %m",
+- polyptr->dir);
+- return PAM_SESSION_ERR;
+- }
++ /*
++ * There is no function mount() that operate on a fd, so instead, we
++ * get the magic link corresponding to the fd and give it to mount().
++ * This protects against potential races exploitable by an unpriv user.
++ */
++ if (pam_sprintf(s_pptrdir, "/proc/self/fd/%d", dfd_pptrdir) < 0) {
++ pam_syslog(idata->pamh, LOG_ERR, "Error pam_sprintf s_pptrdir");
++ goto error_out;
++ }
+
+- if (polyptr->flags & POLYDIR_NOINIT)
+- return PAM_SUCCESS;
++ if (mount("tmpfs", s_pptrdir, "tmpfs", polyptr->mount_flags, polyptr->mount_opts) < 0) {
++ pam_syslog(idata->pamh, LOG_ERR, "Error mounting tmpfs on %s, %m",
++ polyptr->dir);
++ goto error_out;
++ }
++
++ if (polyptr->flags & POLYDIR_NOINIT) {
++ retval = PAM_SUCCESS;
++ goto cleanup;
++ }
+
+- return inst_init(polyptr, "tmpfs", idata, 1);
++ retval = inst_init(polyptr, "tmpfs", idata, 1);
++ goto cleanup;
+ }
+
+- if (stat(polyptr->dir, &statbuf) < 0) {
+- pam_syslog(idata->pamh, LOG_ERR, "Error stating %s: %m",
+- polyptr->dir);
+- return PAM_SESSION_ERR;
++ if (fstat(dfd_pptrdir, &statbuf) < 0) {
++ pam_syslog(idata->pamh, LOG_ERR, "Error stating %s: %m", polyptr->dir);
++ goto error_out;
+ }
+
+ /*
+@@ -1680,15 +1857,16 @@ static int ns_setup(struct polydir_s *polyptr,
+ * security policy.
+ */
+ #ifdef WITH_SELINUX
+- retval = poly_name(polyptr, &instname, &instcontext,
+- &origcontext, idata);
++ retval = poly_name(polyptr, &instcontext, &origcontext, idata);
+ #else
+- retval = poly_name(polyptr, &instname, idata);
++ retval = poly_name(polyptr, idata);
+ #endif
+
+ if (retval != PAM_SUCCESS) {
+- if (retval != PAM_IGNORE)
++ if (retval != PAM_IGNORE) {
+ pam_syslog(idata->pamh, LOG_ERR, "Error getting instance name");
++ goto error_out;
++ }
+ goto cleanup;
+ } else {
+ #ifdef WITH_SELINUX
+@@ -1699,22 +1877,33 @@ static int ns_setup(struct polydir_s *polyptr,
+ #endif
+ }
+
+- if ((inst_dir = pam_asprintf("%s%s", polyptr->instance_prefix, instname)) == NULL)
+- goto error_out;
+-
+- if (idata->flags & PAMNS_DEBUG)
+- pam_syslog(idata->pamh, LOG_DEBUG, "instance_dir %s",
+- inst_dir);
++ /*
++ * Gets a fd in a secure manner (we may be operating on a path under
++ * user control), and check it's compliant.
++ * Then, we should *always* operate on *this* fd and a relative path
++ * to be protected against race conditions.
++ */
++ dfd_iparent = secure_opendir(polyptr->instance_parent,
++ SECURE_OPENDIR_PROTECT | SECURE_OPENDIR_MKDIR, 0, idata);
++ if (dfd_iparent == -1) {
++ pam_syslog(idata->pamh, LOG_ERR,
++ "polyptr->instance_parent %s access error",
++ polyptr->instance_parent);
++ goto error_out;
++ }
++ if (check_inst_parent(dfd_iparent, idata)) {
++ goto error_out;
++ }
+
+ /*
+ * Create instance directory with appropriate security
+ * contexts, owner, group and mode bits.
+ */
+ #ifdef WITH_SELINUX
+- retval = create_instance(polyptr, inst_dir, &statbuf, instcontext,
+- origcontext, idata);
++ retval = create_instance(polyptr, dfd_iparent, &statbuf, instcontext,
++ origcontext, idata);
+ #else
+- retval = create_instance(polyptr, inst_dir, &statbuf, idata);
++ retval = create_instance(polyptr, dfd_iparent, &statbuf, idata);
+ #endif
+
+ if (retval == PAM_IGNORE) {
+@@ -1726,19 +1915,48 @@ static int ns_setup(struct polydir_s *polyptr,
+ goto error_out;
+ }
+
++ /*
++ * Instead of getting a new secure fd, we reuse the fd opened on directory
++ * polyptr->instance_parent to ensure we are working on the same dir as
++ * previously, and thus ensure that previous checks (e.g. check_inst_parent())
++ * are still relevant.
++ */
++ dfd_ipath = openat(dfd_iparent, polyptr->instname,
++ O_PATH | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
++ if (dfd_ipath == -1) {
++ pam_syslog(idata->pamh, LOG_ERR, "Error openat on %s, %m",
++ polyptr->instname);
++ goto error_out;
++ }
++
++ if (pam_sprintf(s_ipath, "/proc/self/fd/%d", dfd_ipath) < 0) {
++ pam_syslog(idata->pamh, LOG_ERR, "Error pam_sprintf s_ipath");
++ goto error_out;
++ }
++
++ if (pam_sprintf(s_pptrdir, "/proc/self/fd/%d", dfd_pptrdir) < 0) {
++ pam_syslog(idata->pamh, LOG_ERR, "Error pam_sprintf s_pptrdir");
++ goto error_out;
++ }
++
+ /*
+ * Bind mount instance directory on top of the polyinstantiated
+ * directory to provide an instance of polyinstantiated directory
+ * based on polyinstantiated method.
++ *
++ * Operates on magic links created from two fd obtained securely
++ * to protect against race conditions and symlink attacks. Indeed,
++ * the source and destination can be in a user controled path.
+ */
+- if (mount(inst_dir, polyptr->dir, NULL, MS_BIND, NULL) < 0) {
+- pam_syslog(idata->pamh, LOG_ERR, "Error mounting %s on %s, %m",
+- inst_dir, polyptr->dir);
++ if(mount(s_ipath, s_pptrdir, NULL, MS_BIND, NULL) < 0) {
++ pam_syslog(idata->pamh, LOG_ERR,
++ "Error mounting %s on %s (%s on %s), %m",
++ s_ipath, s_pptrdir, polyptr->instance_absolute, polyptr->dir);
+ goto error_out;
+ }
+
+ if (!(polyptr->flags & POLYDIR_NOINIT))
+- retval = inst_init(polyptr, inst_dir, idata, newdir);
++ retval = inst_init(polyptr, polyptr->instance_absolute, idata, newdir);
+
+ goto cleanup;
+
+@@ -1750,8 +1968,12 @@ error_out:
+ retval = PAM_SESSION_ERR;
+
+ cleanup:
+- free(inst_dir);
+- free(instname);
++ if (dfd_iparent != -1)
++ close(dfd_iparent);
++ if (dfd_ipath != -1)
++ close(dfd_ipath);
++ if (dfd_pptrdir != -1)
++ close(dfd_pptrdir);
+ #ifdef WITH_SELINUX
+ freecon(instcontext);
+ freecon(origcontext);
+@@ -1790,6 +2012,7 @@ static int cleanup_tmpdirs(struct instance_data *idata)
+ {
+ struct polydir_s *pptr;
+ pid_t rc, pid;
++ int dfd = -1;
+ struct sigaction newsa, oldsa;
+ int status;
+
+@@ -1801,7 +2024,17 @@ static int cleanup_tmpdirs(struct instance_data *idata)
+ }
+
+ for (pptr = idata->polydirs_ptr; pptr; pptr = pptr->next) {
+- if (pptr->method == TMPDIR && access(pptr->instance_prefix, F_OK) == 0) {
++ if (pptr->method == TMPDIR) {
++
++ dfd = secure_opendir_stateless(pptr->instance_parent);
++ if (dfd == -1)
++ continue;
++
++ if (faccessat(dfd, pptr->instname, F_OK, AT_SYMLINK_NOFOLLOW) != 0) {
++ close(dfd);
++ continue;
++ }
++
+ pid = fork();
+ if (pid == 0) {
+ static char *envp[] = { NULL };
+@@ -1811,10 +2044,21 @@ static int cleanup_tmpdirs(struct instance_data *idata)
+ _exit(1);
+ }
+ #endif
++ if (fchdir(dfd) == -1) {
++ pam_syslog(idata->pamh, LOG_ERR, "Failed fchdir to %s: %m",
++ pptr->instance_absolute);
++ _exit(1);
++ }
++
+ close_fds_pre_exec(idata);
+- execle("/bin/rm", "/bin/rm", "-rf", pptr->instance_prefix, NULL, envp);
++
++ execle("/bin/rm", "/bin/rm", "-rf", pptr->instname, NULL, envp);
+ _exit(1);
+ } else if (pid > 0) {
++
++ if (dfd != -1)
++ close(dfd);
++
+ while (((rc = waitpid(pid, &status, 0)) == (pid_t)-1) &&
+ (errno == EINTR));
+ if (rc == (pid_t)-1) {
+@@ -1827,6 +2071,10 @@ static int cleanup_tmpdirs(struct instance_data *idata)
+ "Error removing %s", pptr->instance_prefix);
+ }
+ } else if (pid < 0) {
++
++ if (dfd != -1)
++ close(dfd);
++
+ pam_syslog(idata->pamh, LOG_ERR,
+ "Cannot fork to cleanup temporary directory, %m");
+ rc = PAM_SESSION_ERR;
+@@ -1850,6 +2098,7 @@ out:
+ static int setup_namespace(struct instance_data *idata, enum unmnt_op unmnt)
+ {
+ int retval = 0, need_poly = 0, changing_dir = 0;
++ int dfd = -1;
+ char *cptr, *fptr, poly_parent[PATH_MAX];
+ struct polydir_s *pptr;
+
+@@ -1965,13 +2214,21 @@ static int setup_namespace(struct instance_data *idata, enum unmnt_op unmnt)
+ strcpy(poly_parent, "/");
+ else if (cptr)
+ *cptr = '\0';
+- if (chdir(poly_parent) < 0) {
++
++ dfd = secure_opendir_stateless(poly_parent);
++ if (dfd == -1) {
++ pam_syslog(idata->pamh, LOG_ERR,
++ "Failed opening %s to fchdir: %m", poly_parent);
++ }
++ else if (fchdir(dfd) == -1) {
+ pam_syslog(idata->pamh, LOG_ERR,
+- "Can't chdir to %s, %m", poly_parent);
++ "Failed fchdir to %s: %m", poly_parent);
+ }
++ if (dfd != -1)
++ close(dfd);
+ }
+
+- if (umount(pptr->rdir) < 0) {
++ if (secure_umount(pptr->rdir) < 0) {
+ int saved_errno = errno;
+ pam_syslog(idata->pamh, LOG_ERR, "Unmount of %s failed, %m",
+ pptr->rdir);
+@@ -2041,7 +2298,7 @@ static int orig_namespace(struct instance_data *idata)
+ "Unmounting instance dir for user %d & dir %s",
+ idata->uid, pptr->dir);
+
+- if (umount(pptr->dir) < 0) {
++ if (secure_umount(pptr->dir) < 0) {
+ pam_syslog(idata->pamh, LOG_ERR, "Unmount of %s failed, %m",
+ pptr->dir);
+ return PAM_SESSION_ERR;
+diff --git a/modules/pam_namespace/pam_namespace.h b/modules/pam_namespace/pam_namespace.h
+index 180e042..721d39a 100644
+--- a/modules/pam_namespace/pam_namespace.h
++++ b/modules/pam_namespace/pam_namespace.h
+@@ -121,6 +121,13 @@
+ #define NAMESPACE_POLYDIR_DATA "pam_namespace:polydir_data"
+ #define NAMESPACE_PROTECT_DATA "pam_namespace:protect_data"
+
++/*
++ * Operation mode for function secure_opendir()
++ */
++#define SECURE_OPENDIR_PROTECT 0x00000001
++#define SECURE_OPENDIR_MKDIR 0x00000002
++#define SECURE_OPENDIR_FULL_FD 0x00000004
++
+ /*
+ * Polyinstantiation method options, based on user, security context
+ * or both
+@@ -158,6 +165,9 @@ struct polydir_s {
+ char dir[PATH_MAX]; /* directory to polyinstantiate */
+ char rdir[PATH_MAX]; /* directory to unmount (based on RUSER) */
+ char instance_prefix[PATH_MAX]; /* prefix for instance dir path name */
++ char instance_absolute[PATH_MAX]; /* absolute path to the instance dir (instance_parent + instname) */
++ char instance_parent[PATH_MAX]; /* parent dir of the instance dir */
++ char *instname; /* last segment of the path to the instance dir */
+ enum polymethod method; /* method used to polyinstantiate */
+ unsigned int num_uids; /* number of override uids */
+ uid_t *uid; /* list of override uids */
+--
+2.49.0
+
diff --git a/meta/recipes-extended/pam/libpam/CVE-2025-6020-02.patch b/meta/recipes-extended/pam/libpam/CVE-2025-6020-02.patch
new file mode 100644
index 0000000000..18c2a82fb4
--- /dev/null
+++ b/meta/recipes-extended/pam/libpam/CVE-2025-6020-02.patch
@@ -0,0 +1,187 @@
+From 592d84e1265d04c3104acee815a503856db503a1 Mon Sep 17 00:00:00 2001
+From: Olivier Bal-Petre <olivier.bal-petre@ssi.gouv.fr>
+Date: Tue, 4 Mar 2025 14:37:02 +0100
+Subject: [PATCH] pam_namespace: add flags to indicate path safety
+
+Add two flags in the script to indicate if the paths to the polydir
+and the instance directories are safe (root owned and writable by
+root only).
+
+Signed-off-by: Olivier Bal-Petre <olivier.bal-petre@ssi.gouv.fr>
+Signed-off-by: Dmitry V. Levin <ldv@strace.io>
+
+Upstream-Status: Backport [https://github.com/linux-pam/linux-pam/commit/592d84e1265d04c3104acee815a503856db503a1]
+CVE: CVE-2025-6020
+Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
+---
+ modules/pam_namespace/namespace.init | 56 ++++++++++++-------
+ modules/pam_namespace/pam_namespace.c | 79 ++++++++++++++++++++++++++-
+ 2 files changed, 115 insertions(+), 20 deletions(-)
+
+diff --git a/modules/pam_namespace/namespace.init b/modules/pam_namespace/namespace.init
+index d9053a1..8782178 100755
+--- a/modules/pam_namespace/namespace.init
++++ b/modules/pam_namespace/namespace.init
+@@ -1,25 +1,43 @@
+ #!/bin/sh
+-# It receives polydir path as $1, the instance path as $2,
+-# a flag whether the instance dir was newly created (0 - no, 1 - yes) in $3,
+-# and user name in $4.
++# It receives as arguments:
++# - $1 polydir path (see WARNING below)
++# - $2 instance path (see WARNING below)
++# - $3 flag whether the instance dir was newly created (0 - no, 1 - yes)
++# - $4 user name
++# - $5 flag whether the polydir path ($1) is safe (0 - unsafe, 1 -safe)
++# - $6 flag whether the instance path ($2) is safe (0 - unsafe, 1 - safe)
++#
++# WARNING: This script is invoked with full root privileges. Accessing
++# the polydir ($1) and the instance ($2) directories in this context may be
++# extremely dangerous as those can be under user control. The flags $5 and $6
++# are provided to let you know if all the segments part of the path (except the
++# last one) are owned by root and are writable by root only. If the path does
++# not meet these criteria, you expose yourself to possible symlink attacks when
++# accessing these path.
++# However, even if the path components are safe, the content of the
++# directories may still be owned/writable by a user, so care must be taken!
+ #
+ # The following section will copy the contents of /etc/skel if this is a
+ # newly created home directory.
+-if [ "$3" = 1 ]; then
+- # This line will fix the labeling on all newly created directories
+- [ -x /sbin/restorecon ] && /sbin/restorecon "$1"
+- user="$4"
+- passwd=$(getent passwd "$user")
+- homedir=$(echo "$passwd" | cut -f6 -d":")
+- if [ "$1" = "$homedir" ]; then
+- gid=$(echo "$passwd" | cut -f4 -d":")
+- cp -rT /etc/skel "$homedir"
+- chown -R "$user":"$gid" "$homedir"
+- mask=$(awk '/^UMASK/{gsub("#.*$", "", $2); print $2; exit}' /etc/login.defs)
+- mode=$(printf "%o" $((0777 & ~mask)))
+- chmod ${mode:-700} "$homedir"
+- [ -x /sbin/restorecon ] && /sbin/restorecon -R "$homedir"
+- fi
+-fi
+
++# Executes only if the polydir path is safe
++if [ "$5" = 1 ]; then
++
++ if [ "$3" = 1 ]; then
++ # This line will fix the labeling on all newly created directories
++ [ -x /sbin/restorecon ] && /sbin/restorecon "$1"
++ user="$4"
++ passwd=$(getent passwd "$user")
++ homedir=$(echo "$passwd" | cut -f6 -d":")
++ if [ "$1" = "$homedir" ]; then
++ gid=$(echo "$passwd" | cut -f4 -d":")
++ cp -rT /etc/skel "$homedir"
++ chown -R "$user":"$gid" "$homedir"
++ mask=$(sed -E -n 's/^UMASK[[:space:]]+([^#[:space:]]+).*/\1/p' /etc/login.defs)
++ mode=$(printf "%o" $((0777 & ~mask)))
++ chmod ${mode:-700} "$homedir"
++ [ -x /sbin/restorecon ] && /sbin/restorecon -R "$homedir"
++ fi
++ fi
++fi
+ exit 0
+diff --git a/modules/pam_namespace/pam_namespace.c b/modules/pam_namespace/pam_namespace.c
+index 9d993d4..4c8153b 100644
+--- a/modules/pam_namespace/pam_namespace.c
++++ b/modules/pam_namespace/pam_namespace.c
+@@ -1467,6 +1467,79 @@ static int check_inst_parent(int dfd, struct instance_data *idata)
+ return PAM_SUCCESS;
+ }
+
++/*
++ * Check for a given absolute path that all segments except the last one are:
++ * 1. a directory owned by root and not writable by group or others
++ * 2. a symlink owned by root and referencing a directory respecting 1.
++ * Returns 0 if safe, -1 is unsafe.
++ * If the path is not accessible (does not exist, hidden under a mount...),
++ * returns -1 (unsafe).
++ */
++static int check_safe_path(const char *path, struct instance_data *idata)
++{
++ char *p = strdup(path);
++ char *d;
++ char *dir = p;
++ struct stat st;
++
++ if (p == NULL)
++ return -1;
++
++ /* Check path is absolute */
++ if (p[0] != '/')
++ goto error;
++
++ strip_trailing_slashes(p);
++
++ /* Last segment of the path may be owned by the user */
++ if ((d = strrchr(dir, '/')) != NULL)
++ *d = '\0';
++
++ while ((d=strrchr(dir, '/')) != NULL) {
++
++ /* Do not follow symlinks */
++ if (lstat(dir, &st) != 0)
++ goto error;
++
++ if (S_ISLNK(st.st_mode)) {
++ if (st.st_uid != 0) {
++ if (idata->flags & PAMNS_DEBUG)
++ pam_syslog(idata->pamh, LOG_DEBUG,
++ "Path deemed unsafe: Symlink %s should be owned by root", dir);
++ goto error;
++ }
++
++ /* Follow symlinks */
++ if (stat(dir, &st) != 0)
++ goto error;
++ }
++
++ if (!S_ISDIR(st.st_mode)) {
++ if (idata->flags & PAMNS_DEBUG)
++ pam_syslog(idata->pamh, LOG_DEBUG,
++ "Path deemed unsafe: %s is expected to be a directory", dir);
++ goto error;
++ }
++
++ if (st.st_uid != 0 ||
++ ((st.st_mode & (S_IWGRP|S_IWOTH)) && !(st.st_mode & S_ISVTX))) {
++ if (idata->flags & PAMNS_DEBUG)
++ pam_syslog(idata->pamh, LOG_DEBUG,
++ "Path deemed unsafe: %s should be owned by root, and not be writable by group or others", dir);
++ goto error;
++ }
++
++ *d = '\0';
++ }
++
++ free(p);
++ return 0;
++
++error:
++ free(p);
++ return -1;
++}
++
+ /*
+ * Check to see if there is a namespace initialization script in
+ * the /etc/security directory. If such a script exists
+@@ -1524,7 +1597,11 @@ static int inst_init(const struct polydir_s *polyptr, const char *ipath,
+ close_fds_pre_exec(idata);
+
+ execle(init_script, init_script,
+- polyptr->dir, ipath, newdir?"1":"0", idata->user, NULL, envp);
++ polyptr->dir, ipath,
++ newdir ? "1":"0", idata->user,
++ (check_safe_path(polyptr->dir, idata) == -1) ? "0":"1",
++ (check_safe_path(ipath, idata) == -1) ? "0":"1",
++ NULL, envp);
+ _exit(1);
+ } else if (pid > 0) {
+ while (((rc = waitpid(pid, &status, 0)) == (pid_t)-1) &&
+--
+2.49.0
+
diff --git a/meta/recipes-extended/pam/libpam/CVE-2025-6020-03.patch b/meta/recipes-extended/pam/libpam/CVE-2025-6020-03.patch
new file mode 100644
index 0000000000..238bef47ec
--- /dev/null
+++ b/meta/recipes-extended/pam/libpam/CVE-2025-6020-03.patch
@@ -0,0 +1,35 @@
+From 976c20079358d133514568fc7fd95c02df8b5773 Mon Sep 17 00:00:00 2001
+From: "Dmitry V. Levin" <ldv@strace.io>
+Date: Tue, 27 May 2025 08:00:00 +0000
+Subject: [PATCH] pam_namespace: secure_opendir: do not look at the group
+ ownership
+
+When the directory is not group-writable, the group ownership does
+not matter, and when it is group-writable, there should not be any
+exceptions for the root group as there is no guarantee that the root
+group does not include non-root users.
+
+Upstream-Status: Backport [https://github.com/linux-pam/linux-pam/commit/976c20079358d133514568fc7fd95c02df8b5773]
+CVE: CVE-2025-6020
+Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
+---
+ modules/pam_namespace/pam_namespace.c | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+diff --git a/modules/pam_namespace/pam_namespace.c b/modules/pam_namespace/pam_namespace.c
+index 4c8153b..791dd07 100644
+--- a/modules/pam_namespace/pam_namespace.c
++++ b/modules/pam_namespace/pam_namespace.c
+@@ -215,8 +215,7 @@ static int secure_opendir(const char *path, int opm, mode_t mode,
+ if (dfd_next == -1)
+ goto error;
+ } else if (st.st_uid != 0
+- || (st.st_gid != 0 && (st.st_mode & S_IWGRP))
+- || (st.st_mode & S_IWOTH)) {
++ || (st.st_mode & (S_IWGRP|S_IWOTH))) {
+ /* do not follow symlinks on subdirectories */
+ flags |= O_NOFOLLOW;
+ }
+--
+2.49.0
+
diff --git a/meta/recipes-extended/pam/libpam_1.5.3.bb b/meta/recipes-extended/pam/libpam_1.5.3.bb
index 714cdb6552..815085cc82 100644
--- a/meta/recipes-extended/pam/libpam_1.5.3.bb
+++ b/meta/recipes-extended/pam/libpam_1.5.3.bb
@@ -29,6 +29,11 @@ SRC_URI = "${GITHUB_BASE_URI}/download/v${PV}/Linux-PAM-${PV}.tar.xz \
file://CVE-2024-22365.patch \
file://CVE-2024-10041-1.patch \
file://CVE-2024-10041-2.patch \
+ file://0001-pam-inline-pam-asprintf.patch \
+ file://0002-pam-namespace-rebase.patch \
+ file://CVE-2025-6020-01.patch \
+ file://CVE-2025-6020-02.patch \
+ file://CVE-2025-6020-03.patch \
"
SRC_URI[sha256sum] = "7ac4b50feee004a9fa88f1dfd2d2fa738a82896763050cd773b3c54b0a818283"
--
2.43.0
^ permalink raw reply related [flat|nested] 19+ messages in thread