From nobody Wed Jun 21 05:42:11 2023 X-Original-To: dev-commits-src-branches@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4QmC7R5wlBz4g1pp; Wed, 21 Jun 2023 05:42:11 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4QmC7R4B47z3k6n; Wed, 21 Jun 2023 05:42:11 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1687326131; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=UCsgcWeLu7Mt3atmE4/pq4ZBHvSLJkNlEWKPM76ric4=; b=WHtths7xBcpJUX3kmIqXvAu4OgSg62TK2qkWh8x6d81QQqdoBQZYYFWdjLIdS7OnrF23po b5MtF17err1458eKEvTq6mnIRxM2FUmGx7V8MdMIBfur3YCwSsjwoNAF+3u4Pb0Ws+oqGi o2e+l1z06VgRyyzWseWtHMX4QGB8zn0E7dM+PlquFl/6DVSw0hGPeG6wDy9x92pJEnh8aj Iy6IPpK4A6Hf1wQ1xLNTk7IVEjgTxdLMaFyO8qqBK2S0ycLl/LApWbrz0uL5uQW8JuD9qx FkeIdOE/7X5R7MWgiY5a5OhCpVOxBha7hyI8gO9ZGvyXJ4OkHtlJw1j3lpa1Rw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1687326131; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=UCsgcWeLu7Mt3atmE4/pq4ZBHvSLJkNlEWKPM76ric4=; b=D37mPRs2eKEnSn1iAKPbLpY42WAHuHvTygbGGqhziyf0QBYbr9VJIDOW8WWeClEyVridi5 5vln+Qn0JnFCyeaYWq+jmvOhfzc6xw4U9WeiL4zU4VZUjyDXJUiorr0bFIipQLMH1AoYy8 HZz28pvhh2lHtCB6SkR6EKXS38B5qXTnbAAXQw6f5ae5FbSwqPYNUVpyIzPeokD78lR8Fd i6ZqTiEnMB6NqH9bL0p8Oy8r/TAi2Pp69aZyeJVLAJdap/XYMlGCDur0zo6nYNf/NkIMzT dYKajnPZHf685iYX0TnUd6F56wsyRQ8NB+OiO7GX1NouOPz2CQ0wlEo+ofhVKA== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1687326131; a=rsa-sha256; cv=none; b=UTdAzAH+SUlVQdbKyfuhKWKZEUJCNXlNVwmfNX4roAZ9KrU1jaDJOgFamDqP56AleGX2GV YXl4lgxGq7IFcrMh/VnHYcoEaNQ6hb9YUbKiOGwb9kdfVmvRIVwyzLtYSY/aGmUDaWj7SS kiJxza9WvBCS5YvlfZRABqZFNf0Do/3N4On9rxfHk9AKZvLh6BUCBM9YUF+E8QB73nPhv0 yl6eN4OPuyGo2Vg/A8GeP/yp9MwqTeL1tO1CxEdoip8v1IaYZgvW8saHbm1p5k35i7d7lG 6Jk17B7Z1UOOkxHOiY8Mhqr0QzOs6MkVv8T6Twx0m+yzeygahPmKZX20ywr3yw== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4QmC7R1zvSzVBQ; Wed, 21 Jun 2023 05:42:11 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.16.1/8.16.1) with ESMTP id 35L5gBYA062993; Wed, 21 Jun 2023 05:42:11 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.16.1/8.16.1/Submit) id 35L5gBAc062992; Wed, 21 Jun 2023 05:42:11 GMT (envelope-from git) Date: Wed, 21 Jun 2023 05:42:11 GMT Message-Id: <202306210542.35L5gBAc062992@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org From: Gordon Tetlow Subject: git: 07e3f54f2ea1 - releng/13.1 - pam_krb5: Fix spoofing vulnerability List-Id: Commits to the stable branches of the FreeBSD src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-branches List-Help: List-Post: List-Subscribe: List-Unsubscribe: Sender: owner-dev-commits-src-branches@freebsd.org X-BeenThere: dev-commits-src-branches@freebsd.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: gordon X-Git-Repository: src X-Git-Refname: refs/heads/releng/13.1 X-Git-Reftype: branch X-Git-Commit: 07e3f54f2ea1a9c5c5e643155994eeec912d16d7 Auto-Submitted: auto-generated X-ThisMailContainsUnwantedMimeParts: N The branch releng/13.1 has been updated by gordon: URL: https://cgit.FreeBSD.org/src/commit/?id=07e3f54f2ea1a9c5c5e643155994eeec912d16d7 commit 07e3f54f2ea1a9c5c5e643155994eeec912d16d7 Author: Cy Schubert AuthorDate: 2023-05-31 19:20:27 +0000 Commit: Gordon Tetlow CommitDate: 2023-06-21 05:27:22 +0000 pam_krb5: Fix spoofing vulnerability An adversary on the network can log in via ssh as any user by spoofing the KDC. When the machine has a keytab installed the keytab is used to verify the service ticket. However, without a keytab there is no way for pam_krb5 to verify the KDC's response and get a TGT with the password. If both the password _and_ the KDC are controlled by an adversary, the adversary can provide a password that the adversary's spoofed KDC will return a valid tgt for. Currently, without a keytab, pam_krb5 is vulnerable to this attack. Reported by: Taylor R Campbell via emaste@ Reviewed by: so Approved by: so Security: FreeBSD-SA-23:04.pam_krb5 Security: CVE-2023-3326 (cherry picked from commit 813847e49e35439ba5d7bf16034b0691312068a4) (cherry picked from commit 6322a6c9daaabbf0b5d17c5d5a4f245f474a7e30) --- lib/libpam/modules/pam_krb5/pam_krb5.8 | 15 +++++ lib/libpam/modules/pam_krb5/pam_krb5.c | 104 +++++++++++++++++++++++++++------ 2 files changed, 102 insertions(+), 17 deletions(-) diff --git a/lib/libpam/modules/pam_krb5/pam_krb5.8 b/lib/libpam/modules/pam_krb5/pam_krb5.8 index bd7ac5b9ca0c..bdd91c54fce6 100644 --- a/lib/libpam/modules/pam_krb5/pam_krb5.8 +++ b/lib/libpam/modules/pam_krb5/pam_krb5.8 @@ -108,6 +108,21 @@ and .Ql %p , to designate the current process ID; can be used in .Ar name . +.It Cm allow_kdc_spoof +Allow +.Nm +to succeed even if there is no host or service key available in a +keytab to authenticate the Kerberos KDC's ticket. +If there is no such key, for example on a host with no keytabs, +.Nm +will fail immediately without prompting the user. +.Pp +.Sy Warning : +If the host has not been configured with a keytab from the KDC, setting +this option makes it vulnerable to malicious KDCs, e.g. via DNS +flooding, because +.Nm +has no way to distinguish the legitimate KDC from a spoofed KDC. .It Cm no_user_check Do not verify if a user exists on the local system. This option implies the .Cm no_ccache diff --git a/lib/libpam/modules/pam_krb5/pam_krb5.c b/lib/libpam/modules/pam_krb5/pam_krb5.c index 810573bed47e..3972479a581f 100644 --- a/lib/libpam/modules/pam_krb5/pam_krb5.c +++ b/lib/libpam/modules/pam_krb5/pam_krb5.c @@ -76,7 +76,12 @@ __FBSDID("$FreeBSD$"); #define COMPAT_HEIMDAL /* #define COMPAT_MIT */ -static int verify_krb_v5_tgt(krb5_context, krb5_ccache, char *, int); +static int verify_krb_v5_tgt_begin(krb5_context, char *, int, + const char **, krb5_principal *, char[static BUFSIZ]); +static int verify_krb_v5_tgt(krb5_context, krb5_ccache, char *, int, + const char *, krb5_principal, char[static BUFSIZ]); +static void verify_krb_v5_tgt_cleanup(krb5_context, int, + const char *, krb5_principal, char[static BUFSIZ]); static void cleanup_cache(pam_handle_t *, void *, int); static const char *compat_princ_component(krb5_context, krb5_principal, int); static void compat_free_data_contents(krb5_context, krb5_data *); @@ -92,6 +97,7 @@ static void compat_free_data_contents(krb5_context, krb5_data *); #define PAM_OPT_NO_USER_CHECK "no_user_check" #define PAM_OPT_REUSE_CCACHE "reuse_ccache" #define PAM_OPT_NO_USER_CHECK "no_user_check" +#define PAM_OPT_ALLOW_KDC_SPOOF "allow_kdc_spoof" #define PAM_LOG_KRB5_ERR(ctx, rv, fmt, ...) \ do { \ @@ -109,6 +115,10 @@ pam_sm_authenticate(pam_handle_t *pamh, int flags __unused, { krb5_error_code krbret; krb5_context pam_context; + int debug; + const char *auth_service; + krb5_principal auth_princ; + char auth_phost[BUFSIZ]; krb5_creds creds; krb5_principal princ; krb5_ccache ccache; @@ -139,14 +149,37 @@ pam_sm_authenticate(pam_handle_t *pamh, int flags __unused, PAM_LOG("Got service: %s", (const char *)service); + if ((srvdup = strdup(service)) == NULL) { + retval = PAM_BUF_ERR; + goto cleanup6; + } + krbret = krb5_init_context(&pam_context); if (krbret != 0) { PAM_VERBOSE_ERROR("Kerberos 5 error"); - return (PAM_SERVICE_ERR); + retval = PAM_SERVICE_ERR; + goto cleanup5; } PAM_LOG("Context initialised"); + debug = openpam_get_option(pamh, PAM_OPT_DEBUG) ? 1 : 0; + krbret = verify_krb_v5_tgt_begin(pam_context, srvdup, debug, + &auth_service, &auth_princ, auth_phost); + if (krbret != 0) { /* failed to find key */ + /* Keytab or service key does not exist */ + /* + * Give up now because we can't authenticate the KDC + * with a keytab, unless the administrator asked to + * have the traditional behaviour of being vulnerable + * to spoofed KDCs. + */ + if (!openpam_get_option(pamh, PAM_OPT_ALLOW_KDC_SPOOF)) { + retval = PAM_SERVICE_ERR; + goto cleanup4; + } + } + krbret = krb5_cc_register(pam_context, &krb5_mcc_ops, FALSE); if (krbret != 0 && krbret != KRB5_CC_TYPE_EXISTS) { PAM_VERBOSE_ERROR("Kerberos 5 error"); @@ -292,13 +325,11 @@ pam_sm_authenticate(pam_handle_t *pamh, int flags __unused, PAM_LOG("Credentials stashed"); /* Verify them */ - if ((srvdup = strdup(service)) == NULL) { - retval = PAM_BUF_ERR; - goto cleanup; - } krbret = verify_krb_v5_tgt(pam_context, ccache, srvdup, - openpam_get_option(pamh, PAM_OPT_DEBUG) ? 1 : 0); + debug, + auth_service, auth_princ, auth_phost); free(srvdup); + srvdup = NULL; if (krbret == -1) { PAM_VERBOSE_ERROR("Kerberos 5 error"); krb5_cc_destroy(pam_context, ccache); @@ -349,8 +380,20 @@ cleanup3: PAM_LOG("Done cleanup3"); +cleanup4: + verify_krb_v5_tgt_cleanup(pam_context, debug, + auth_service, auth_princ, auth_phost); + PAM_LOG("Done cleanup4"); + +cleanup5: + if (srvdup != NULL) + free(srvdup); + PAM_LOG("Done cleanup5"); + +cleanup6: if (retval != PAM_SUCCESS) PAM_VERBOSE_ERROR("Kerberos 5 refuses you"); + PAM_LOG("Done cleanup6"); return (retval); } @@ -837,18 +880,18 @@ PAM_MODULE_ENTRY("pam_krb5"); */ /* ARGSUSED */ static int -verify_krb_v5_tgt(krb5_context context, krb5_ccache ccache, - char *pam_service, int debug) +verify_krb_v5_tgt_begin(krb5_context context, char *pam_service, int debug, + const char **servicep, krb5_principal *princp __unused, char phost[static BUFSIZ]) { krb5_error_code retval; krb5_principal princ; krb5_keyblock *keyblock; - krb5_data packet; - krb5_auth_context auth_context; - char phost[BUFSIZ]; const char *services[3], **service; - packet.data = 0; + *servicep = NULL; + + if (debug) + openlog("pam_krb5", LOG_PID, LOG_AUTHPRIV); /* If possible we want to try and verify the ticket we have * received against a keytab. We will try multiple service @@ -906,14 +949,30 @@ verify_krb_v5_tgt(krb5_context context, krb5_ccache ccache, krb5_free_error_message(context, msg); } retval = 0; - goto cleanup; } if (keyblock) krb5_free_keyblock(context, keyblock); + return (retval); +} + +static int +verify_krb_v5_tgt(krb5_context context, krb5_ccache ccache, + char *pam_service __unused, int debug, + const char *service, krb5_principal princ, char phost[static BUFSIZ]) +{ + krb5_error_code retval; + krb5_auth_context auth_context = NULL; + krb5_data packet; + + if (service == NULL) + return (0); /* uncertain, can't authenticate KDC */ + + packet.data = 0; + /* Talk to the kdc and construct the ticket. */ auth_context = NULL; - retval = krb5_mk_req(context, &auth_context, 0, *service, phost, + retval = krb5_mk_req(context, &auth_context, 0, service, phost, NULL, ccache, &packet); if (auth_context) { krb5_auth_con_free(context, auth_context); @@ -952,8 +1011,19 @@ verify_krb_v5_tgt(krb5_context context, krb5_ccache ccache, cleanup: if (packet.data) compat_free_data_contents(context, &packet); - krb5_free_principal(context, princ); - return retval; + return (retval); +} + +static void +verify_krb_v5_tgt_cleanup(krb5_context context, int debug, + const char *service, krb5_principal princ, char phost[static BUFSIZ] __unused) +{ + + if (service) + krb5_free_principal(context, princ); + if (debug) + closelog(); + } /* Free the memory for cache_name. Called by pam_end() */