Package: release.debian.org Severity: normal Tags: wheezy User: [email protected] Usertags: pu
Hi, I'd like to fix CVE-2015-3206 (missing KDC authenticity verification) for wheezy via a point release. The debdiff is attached. The bug is fixed in unstable as well as squeeze-lts already. As in squeeze-lts the KDC check is disabled by default to not break existing installations. Cheers, -- Guido. -- System Information: Debian Release: 8.1 APT prefers stable APT policy: (990, 'stable'), (500, 'stable-updates'), (500, 'unstable'), (500, 'testing'), (1, 'experimental') Architecture: amd64 (x86_64) Foreign Architectures: i386 Kernel: Linux 4.1.0-2-amd64 (SMP w/4 CPU cores) Locale: LANG=de_DE.UTF-8, LC_CTYPE=de_DE.UTF-8 (charmap=UTF-8) Shell: /bin/sh linked to /bin/dash Init: systemd (via /run/systemd/system)
>From 196366a8fbf843cc91f88aa17eed7aff2591b693 Mon Sep 17 00:00:00 2001 Message-Id: <196366a8fbf843cc91f88aa17eed7aff2591b693.1441460328.git....@sigxcpu.org> From: =?UTF-8?q?Guido=20G=C3=BCnther?= <[email protected]> Date: Sat, 5 Sep 2015 15:32:08 +0200 Subject: [PATCH] Forward port fix for CVE-2015-3206 to wheezy from squeeze-lts --- bin/login | 47 +++++++++++++++++++++++++++++++++++++++++++++++ debian/NEWS | 42 ++++++++++++++++++++++++++++++++++++++++++ debian/changelog | 9 +++++++++ debian/examples | 1 + pysrc/kerberos.py | 4 +++- src/kerberos.c | 5 +++-- src/kerberosbasic.c | 41 ++++++++++++++++++++++++++++++++++------- src/kerberosbasic.h | 2 +- 8 files changed, 140 insertions(+), 11 deletions(-) create mode 100644 bin/login create mode 100644 debian/NEWS diff --git a/bin/login b/bin/login new file mode 100644 index 0000000..d9becaa --- /dev/null +++ b/bin/login @@ -0,0 +1,47 @@ +#!/usr/bin/env python +## +# Copyright (c) 2008 Jelmer Vernooij <[email protected]> +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""A sample script showing how to verify a user with username and password.""" + +import argparse +import getpass +import kerberos +import sys + + +def main(): + # construct arg parser + parser = argparse.ArgumentParser(description='Verify Kerberos login.') + parser.add_argument('--service', default='', help='the service to authenticate as') + parser.add_argument('--default_realm', default='', help='the realm to use if none is provided in the username') + parser.add_argument('--verify', default=False, action='store_true', help='enable KDC verification support') + parser.add_argument('user', metavar='user', help='the username to verify') + args = parser.parse_args() + + print args.service + + # get the password + password = getpass.getpass('Password: ') + + # verify + try: + kerberos.checkPassword(args.user, password, args.service, args.default_realm, args.verify) + print "Successfully verified" + except kerberos.BasicAuthError as e: + print "{0} ({1})".format(*e.args) + +if __name__ == "__main__": + main() diff --git a/debian/NEWS b/debian/NEWS new file mode 100644 index 0000000..2143663 --- /dev/null +++ b/debian/NEWS @@ -0,0 +1,42 @@ +pykerberos (1.1+svn4895-1+deb7u1) wheezy; urgency=medium + + The python-kerberos checkPassword() method has been badly insecure in + previous releases. It used to do (and still does by default) a kinit + (AS-REQ) to ask a KDC for a TGT for the given user principal, and + interprets the success or failure of that as indicating whether the + password is correct. It does not, however, verify that it actually spoke + to a trusted KDC: an attacker may simply reply instead with an AS-REP + which matches the password he just gave you. + . + Imagine you were verifying a password using LDAP authentication rather + than Kerberos: you would, of course, use TLS in conjunction with LDAP to + make sure you were talking to a real, trusted LDAP server. The same + requirement applies here. kinit is not a password-verification service. + . + The usual way of doing this is to take the TGT you've obtained with the + user's password, and then obtain a ticket for a principal for which the + verifier has keys (e.g. a web server processing a username/password form + login might get a ticket for its own HTTP/host@REALM principal), which + it can then verify. Note that this requires that the verifier has its + own Kerberos identity, which is mandated by the symmetric nature of + Kerberos (whereas in the LDAP case, the use of public-key cryptography + allows anonymous verification). + . + The fact of pykerberos being susceptible to KDC spoofing attacks has + been filed as CVE-2015-3206. + . + With this version of the pykerberos package a new option is introduced + for the checkPassword() method. Setting verify to True when using + checkPassword() will perform a KDC verification. For this to work, you + need to provide a krb5.keytab file containing service principal keys for + the service you intend to use. + . + As the default krb5.keytab file in /etc is normally not accessible by + non-root users/processes, you have to make sure a custom krb5.keytab + file containing the correct principal keys is provided to your + application using the KRB5_KTNAME environment variable. + . + Note: In Debian wheezy, KDC verification support is disabled by + default in ordner not to break existing setups. + + -- Guido Günther <[email protected]> Sat, 05 Sep 2015 15:31:19 +0200 diff --git a/debian/changelog b/debian/changelog index 27c11ff..f0076d2 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,12 @@ +pykerberos (1.1+svn4895-1+deb7u1) wheezy; urgency=medium + + * Add KDC authenticity verification support (CVE-2015-3206). + Obtained from upstream, ignoring white-space changes, URL: + https://github.com/02strich/pykerberos/commit/ + 02d13860b25fab58e739f0e000bed0067b7c6f9c + + -- Guido Günther <[email protected]> Sat, 05 Sep 2015 15:31:19 +0200 + pykerberos (1.1+svn4895-1) unstable; urgency=low * [d6e470d] fix typo in package description (Closes: #520276) - thanks to diff --git a/debian/examples b/debian/examples index de45608..10845a7 100644 --- a/debian/examples +++ b/debian/examples @@ -1 +1,2 @@ bin/ftp-gss +bin/login diff --git a/pysrc/kerberos.py b/pysrc/kerberos.py index eb8839f..a6c4c3a 100644 --- a/pysrc/kerberos.py +++ b/pysrc/kerberos.py @@ -27,7 +27,7 @@ class BasicAuthError(KrbError): class GSSError(KrbError): pass -def checkPassword(user, pswd, service, default_realm): +def checkPassword(user, pswd, service, default_realm, verify=False): """ This function provides a simple way to verify that a user name and password match those normally used for Kerberos authentication. It does this by checking that the @@ -49,6 +49,8 @@ def checkPassword(user, pswd, service, default_realm): @param default_realm: a string containing the default realm to use if one is not supplied in the user argument. Note that Kerberos realms are normally all uppercase (e.g., 'EXAMPLE.COM'). + @param verify: a boolean flagging KDC verification to enabled or disabled + (default: False). @return: True if authentication succeeds, False otherwise. """ diff --git a/src/kerberos.c b/src/kerberos.c index e0d8938..a887f7c 100644 --- a/src/kerberos.c +++ b/src/kerberos.c @@ -31,12 +31,13 @@ static PyObject *checkPassword(PyObject *self, PyObject *args) const char *pswd; const char *service; const char *default_realm; + int verify = 0; int result = 0; - if (!PyArg_ParseTuple(args, "ssss", &user, &pswd, &service, &default_realm)) + if (!PyArg_ParseTuple(args, "ssss|b", &user, &pswd, &service, &default_realm, &verify)) return NULL; - result = authenticate_user_krb5pwd(user, pswd, service, default_realm); + result = authenticate_user_krb5pwd(user, pswd, service, default_realm, verify); if (result) return Py_INCREF(Py_True), Py_True; diff --git a/src/kerberosbasic.c b/src/kerberosbasic.c index 8b38f11..65ec176 100644 --- a/src/kerberosbasic.c +++ b/src/kerberosbasic.c @@ -26,9 +26,9 @@ extern PyObject *BasicAuthException_class; static void set_basicauth_error(krb5_context context, krb5_error_code code); -static krb5_error_code verify_krb5_user(krb5_context context, krb5_principal principal, const char *password, krb5_principal server); +static krb5_error_code verify_krb5_user(krb5_context context, krb5_principal principal, const char *password, krb5_principal server, unsigned char verify); -int authenticate_user_krb5pwd(const char *user, const char *pswd, const char *service, const char *default_realm) +int authenticate_user_krb5pwd(const char *user, const char *pswd, const char *service, const char *default_realm, unsigned char verify) { krb5_context kcontext = NULL; krb5_error_code code; @@ -87,7 +87,7 @@ int authenticate_user_krb5pwd(const char *user, const char *pswd, const char *se goto end; } - code = verify_krb5_user(kcontext, client, pswd, server); + code = verify_krb5_user(kcontext, client, pswd, server, verify); if (code) { @@ -113,10 +113,11 @@ end: } /* Inspired by krb5_verify_user from Heimdal */ -static krb5_error_code verify_krb5_user(krb5_context context, krb5_principal principal, const char *password, krb5_principal server) +static krb5_error_code verify_krb5_user(krb5_context context, krb5_principal principal, const char *password, krb5_principal server, unsigned char verify) { krb5_creds creds; - krb5_get_init_creds_opt gic_options; + krb5_get_init_creds_opt *gic_options; + krb5_verify_init_creds_opt vic_options; krb5_error_code ret; char *name = NULL; @@ -131,17 +132,43 @@ static krb5_error_code verify_krb5_user(krb5_context context, krb5_principal pri free(name); } - krb5_get_init_creds_opt_init(&gic_options); - ret = krb5_get_init_creds_password(context, &creds, principal, (char *)password, NULL, NULL, 0, NULL, &gic_options); + // verify passed in server principal if needed + if (verify) { + ret = krb5_unparse_name(context, server, &name); + if (ret == 0) { +#ifdef PRINTFS + printf("Trying to get TGT for service %s\n", name); +#endif + free(name); + } + } + + // verify password + krb5_get_init_creds_opt_alloc(context, &gic_options); + ret = krb5_get_init_creds_password(context, &creds, principal, (char *)password, NULL, NULL, 0, NULL, gic_options); if (ret) { set_basicauth_error(context, ret); goto end; } + // verify response authenticity + if (verify) { + krb5_verify_init_creds_opt_init(&vic_options); + krb5_verify_init_creds_opt_set_ap_req_nofail(&vic_options, 1); + ret = krb5_verify_init_creds(context, &creds, server, NULL, NULL, &vic_options); + if (ret) { + set_basicauth_error(context, ret); + } + } + end: + // clean up krb5_free_cred_contents(context, &creds); + if (gic_options) + krb5_get_init_creds_opt_free(context, gic_options); + return ret; } diff --git a/src/kerberosbasic.h b/src/kerberosbasic.h index d3dca95..116d93d 100644 --- a/src/kerberosbasic.h +++ b/src/kerberosbasic.h @@ -20,4 +20,4 @@ #define krb5_get_err_text(context,code) error_message(code) -int authenticate_user_krb5pwd(const char *user, const char *pswd, const char *service, const char *default_realm); +int authenticate_user_krb5pwd(const char *user, const char *pswd, const char *service, const char *default_realm, unsigned char verify); -- 2.1.4

