summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--WHATSNEW.txt13
-rw-r--r--nsswitch/krb5_plugin/winbind_krb5_localauth.c267
-rw-r--r--nsswitch/wscript_build6
-rw-r--r--wscript_configure_system_mitkrb51
4 files changed, 287 insertions, 0 deletions
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..7c77609710a
--- /dev/null
+++ b/nsswitch/krb5_plugin/winbind_krb5_localauth.c
@@ -0,0 +1,267 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ A localauth plugin for MIT Kerberos
+
+ Copyright (C) 2018 Andreas Schneider <asn@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>
+#if HAVE_COM_ERR_H
+#include <com_err.h>
+#endif
+
+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) {
+ com_err("winbind_localauth",
+ EINVAL,
+ "Failed to load, plugin API changed.");
+ 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')