diff options
-rw-r--r-- | WHATSNEW.txt | 13 | ||||
-rw-r--r-- | nsswitch/krb5_plugin/winbind_krb5_localauth.c | 267 | ||||
-rw-r--r-- | nsswitch/wscript_build | 6 | ||||
-rw-r--r-- | wscript_configure_system_mitkrb5 | 1 |
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') |