[PATCH] Add a winbind localauth plugin for MIT Kerberos

Andreas Schneider asn at samba.org
Thu Jun 21 08:21:51 UTC 2018


Hi,

attached is a patch which implements a localauth plugin for MIT Kerberos.

https://web.mit.edu/kerberos/krb5-latest/doc/plugindev/localauth.html

https://bugzilla.samba.org/show_bug.cgi?id=13480


Please review.


Thanks,



	Andreas

-- 
Andreas Schneider                      asn at samba.org
Samba Team                             www.samba.org
GPG-ID:     8DFF53E18F2ABC8D8F3C92237EE0FC4DCC014E3D
-------------- next part --------------
>From 900023cefa6ee039cfd2ca01e1e17b0d88a9036f Mon Sep 17 00:00:00 2001
From: Andreas Schneider <asn at samba.org>
Date: Fri, 15 Jun 2018 14:59:00 +0200
Subject: [PATCH] krb5_plugin: Add winbind localauth plugin for MIT Kerberos

https://web.mit.edu/kerberos/krb5-latest/doc/plugindev/localauth.html

BUG: https://bugzilla.samba.org/show_bug.cgi?id=13480

Signed-off-by: Andreas Schneider <asn at samba.org>
---
 WHATSNEW.txt                                  |  13 +
 nsswitch/krb5_plugin/winbind_krb5_localauth.c | 261 ++++++++++++++++++
 nsswitch/wscript_build                        |   6 +
 wscript_configure_system_mitkrb5              |   1 +
 4 files changed, 281 insertions(+)
 create mode 100644 nsswitch/krb5_plugin/winbind_krb5_localauth.c

diff --git a/WHATSNEW.txt b/WHATSNEW.txt
index b9c80cf9d80..2ceacc41995 100644
--- a/WHATSNEW.txt
+++ b/WHATSNEW.txt
@@ -53,6 +53,19 @@ net ads keytab create no longer tries to generate SPN(s) from existing
 entries in a keytab file. If it is required to add Windows SPN(s) then
 'net ads setspn add' should be used instead.
 
+Local authorization plugin for MIT Kerberos
+-------------------------------------------
+
+This plugin controls the relationship between Kerberos principals and AD
+accounts through winbind. The module receives the Kerberos principal and the
+local account name as inputs and can then check if they match. This can resolve
+issues with canonicalized names returned by Kerberos within AD. If the user
+tries to log in as 'alice', but the samAccountName is set to ALICE (uppercase),
+Kerberos would return ALICE as the username. Kerberos would not be able to map
+'alice' to 'ALICE' in this case and auth would fail.  With this plugin account
+names can be correctly mapped. This only applies to GSSAPI authentication,
+not for the geting the initial ticket granting ticket.
+
 REMOVED FEATURES
 ================
 
diff --git a/nsswitch/krb5_plugin/winbind_krb5_localauth.c b/nsswitch/krb5_plugin/winbind_krb5_localauth.c
new file mode 100644
index 00000000000..aa7befa4b93
--- /dev/null
+++ b/nsswitch/krb5_plugin/winbind_krb5_localauth.c
@@ -0,0 +1,261 @@
+/*
+   Unix SMB/CIFS implementation.
+
+   A localauth plugin for MIT Kerberos
+
+   Copyright (C) 2018      Andreas Schneider <asn at samba.org>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "replace.h"
+#include <krb5/localauth_plugin.h>
+#include <wbclient.h>
+
+struct krb5_localauth_moddata_st {
+	struct wbcContext *wbc_ctx;
+};
+
+/*
+ * Initialize the module data.
+ *
+ * This creates the wbclient context.
+ */
+static krb5_error_code winbind_init(krb5_context context,
+				    krb5_localauth_moddata *data)
+{
+	krb5_localauth_moddata d;
+
+	*data = NULL;
+	d = malloc(sizeof(struct krb5_localauth_moddata_st));
+	if (d == NULL) {
+		return ENOMEM;
+	}
+
+	d->wbc_ctx = wbcCtxCreate();
+	if (d->wbc_ctx == NULL) {
+		free(d);
+		return ENOMEM;
+	}
+
+	*data = d;
+
+	return 0;
+}
+
+/*
+ * Release resources used by module data.
+ */
+static void winbind_fini(krb5_context context, krb5_localauth_moddata data)
+{
+	wbcCtxFree(data->wbc_ctx);
+	free(data);
+	data = NULL;
+}
+
+/*
+ * Determine whether aname is authorized to log in as the local account lname.
+ *
+ * Return 0 if aname is authorized, EPERM if aname is authoritatively not
+ * authorized, KRB5_PLUGIN_NO_HANDLE if the module cannot determine whether
+ * aname is authorized, and any other error code for a serious failure to
+ * process the request.  aname will be considered authorized if at least one
+ * module returns 0 and all other modules return KRB5_PLUGIN_NO_HANDLE.
+ */
+static krb5_error_code winbind_userok(krb5_context context,
+				      krb5_localauth_moddata data,
+				      krb5_const_principal aname,
+				      const char *lname)
+{
+	krb5_error_code code = 0;
+	char *princ_str = NULL;
+	struct passwd *pwd = NULL;
+	uid_t princ_uid;
+	uid_t lname_uid;
+	wbcErr wbc_status;
+	int cmp;
+
+	code = krb5_unparse_name(context, aname, &princ_str);
+	if (code != 0) {
+		return code;
+	}
+
+	cmp = strcasecmp(princ_str, lname);
+	if (cmp == 0) {
+		krb5_free_unparsed_name(context, princ_str);
+		return 0;
+	}
+
+	wbc_status = wbcCtxGetpwnam(data->wbc_ctx,
+				    princ_str,
+				    &pwd);
+	krb5_free_unparsed_name(context, princ_str);
+	switch (wbc_status) {
+	case WBC_ERR_SUCCESS:
+		princ_uid = pwd->pw_uid;
+		code = 0;
+		break;
+	case WBC_ERR_UNKNOWN_USER:
+	/* match other insane libwbclient return codes */
+	case WBC_ERR_WINBIND_NOT_AVAILABLE:
+	case WBC_ERR_DOMAIN_NOT_FOUND:
+		code = KRB5_PLUGIN_NO_HANDLE;
+		break;
+	default:
+		code = EIO;
+		break;
+	}
+	wbcFreeMemory(pwd);
+	if (code != 0) {
+		return code;
+	}
+
+	wbc_status = wbcCtxGetpwnam(data->wbc_ctx,
+				    lname,
+				    &pwd);
+	switch (wbc_status) {
+	case WBC_ERR_SUCCESS:
+		lname_uid = pwd->pw_uid;
+		break;
+	case WBC_ERR_UNKNOWN_USER:
+	/* match other insane libwbclient return codes */
+	case WBC_ERR_WINBIND_NOT_AVAILABLE:
+	case WBC_ERR_DOMAIN_NOT_FOUND:
+		code = KRB5_PLUGIN_NO_HANDLE;
+		break;
+	default:
+		code = EIO;
+		break;
+	}
+	wbcFreeMemory(pwd);
+	if (code != 0) {
+		return code;
+	}
+
+	if (princ_uid != lname_uid) {
+		code = EPERM;
+	}
+
+	return code;
+}
+
+/*
+ * Determine the local account name corresponding to aname.
+ *
+ * Return 0 and set *lname_out if a mapping can be determined; the contents of
+ * *lname_out will later be released with a call to the module's free_string
+ * method.  Return KRB5_LNAME_NOTRANS if no mapping can be determined.  Return
+ * any other error code for a serious failure to process the request; this will
+ * halt the krb5_aname_to_localname operation.
+ *
+ * If the module's an2ln_types field is set, this method will only be invoked
+ * when a profile "auth_to_local" value references one of the module's types.
+ * type and residual will be set to the type and residual of the auth_to_local
+ * value.
+ *
+ * If the module's an2ln_types field is not set but the an2ln method is
+ * implemented, this method will be invoked independently of the profile's
+ * auth_to_local settings, with type and residual set to NULL.  If multiple
+ * modules are registered with an2ln methods but no an2ln_types field, the
+ * order of invocation is not defined, but all such modules will be consulted
+ * before the built-in mechanisms are tried.
+ */
+static krb5_error_code winbind_an2ln(krb5_context context,
+				     krb5_localauth_moddata data,
+				     const char *type,
+				     const char *residual,
+				     krb5_const_principal aname,
+				     char **lname_out)
+{
+	krb5_error_code code = 0;
+	char *princ_str = NULL;
+	char *name = NULL;
+	struct passwd *pwd = NULL;
+	wbcErr wbc_status;
+
+	code = krb5_unparse_name(context, aname, &princ_str);
+	if (code != 0) {
+		return code;
+	}
+
+	wbc_status = wbcCtxGetpwnam(data->wbc_ctx,
+				    princ_str,
+				    &pwd);
+	krb5_free_unparsed_name(context, princ_str);
+	switch (wbc_status) {
+	case WBC_ERR_SUCCESS:
+		name = strdup(pwd->pw_name);
+		code = 0;
+		break;
+	case WBC_ERR_UNKNOWN_USER:
+	/* match other insane libwbclient return codes */
+	case WBC_ERR_WINBIND_NOT_AVAILABLE:
+	case WBC_ERR_DOMAIN_NOT_FOUND:
+		code = KRB5_LNAME_NOTRANS;
+		break;
+	default:
+		code = EIO;
+		break;
+	}
+	wbcFreeMemory(pwd);
+	if (code != 0) {
+		return code;
+	}
+
+	if (name == NULL) {
+		return ENOMEM;
+	}
+
+	*lname_out = name;
+
+	return code;
+}
+
+/*
+ * Release the memory returned by an invocation of an2ln.
+ */
+static void winbind_free_string(krb5_context context,
+				krb5_localauth_moddata data,
+				char *str)
+{
+	free(str);
+}
+
+krb5_error_code
+localauth_winbind_initvt(krb5_context context,
+			 int maj_ver,
+			 int min_ver,
+			 krb5_plugin_vtable vtable);
+
+krb5_error_code
+localauth_winbind_initvt(krb5_context context,
+			 int maj_ver,
+			 int min_ver,
+			 krb5_plugin_vtable vtable)
+{
+	krb5_localauth_vtable vt = (krb5_localauth_vtable)vtable;
+
+	if (maj_ver != 1 || min_ver != 1) {
+		return KRB5_PLUGIN_VER_NOTSUPP;
+	}
+
+	vt->init = winbind_init;
+	vt->fini = winbind_fini;
+	vt->name = "winbind";
+	vt->an2ln = winbind_an2ln;
+	vt->userok = winbind_userok;
+	vt->free_string = winbind_free_string;
+
+	return 0;
+}
diff --git a/nsswitch/wscript_build b/nsswitch/wscript_build
index ab8f8eaf270..15e93db2f05 100644
--- a/nsswitch/wscript_build
+++ b/nsswitch/wscript_build
@@ -110,6 +110,12 @@ if bld.CONFIG_SET('HAVE_KRB5_LOCATE_PLUGIN_H'):
 		deps='wbclient krb5 com_err',
 		realname='winbind_krb5_locator.so')
 
+if bld.CONFIG_SET('HAVE_KRB5_LOCALAUTH_PLUGIN_H'):
+    bld.SAMBA_LIBRARY('winbind_krb5_localauth',
+                      source='krb5_plugin/winbind_krb5_localauth.c',
+                      deps='wbclient krb5 com_err',
+                      realname='winbind-krb5-localauth.so')
+
 bld.SAMBA_SUBSYSTEM('WB_REQTRANS',
 	source='wb_reqtrans.c',
 	deps='talloc tevent LIBASYNC_REQ'
diff --git a/wscript_configure_system_mitkrb5 b/wscript_configure_system_mitkrb5
index 803dad7ab63..facf415e308 100644
--- a/wscript_configure_system_mitkrb5
+++ b/wscript_configure_system_mitkrb5
@@ -80,6 +80,7 @@ conf.CHECK_HEADERS('com_err.h', lib='com_err')
 conf.CHECK_HEADERS('kdb.h', lib='kdb5')
 
 conf.CHECK_HEADERS('krb5.h krb5/locate_plugin.h', lib='krb5')
+conf.CHECK_HEADERS('krb5.h krb5/localauth_plugin.h', lib='krb5')
 possible_gssapi_headers="gssapi.h gssapi/gssapi_generic.h gssapi/gssapi.h gssapi/gssapi_ext.h gssapi/gssapi_krb5.h gssapi/gssapi_oid.h"
 conf.CHECK_HEADERS(possible_gssapi_headers, lib='gssapi')
 
-- 
2.17.1



More information about the samba-technical mailing list