Add mschap module winbind authentication
authorMatthew Newton <mcn4@leicester.ac.uk>
Mon, 16 Mar 2015 23:33:01 +0000 (23:33 +0000)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Sat, 21 Mar 2015 22:17:08 +0000 (18:17 -0400)
This allows AD/Samba authentication against winbind
via libwbclient without having to use ntlm_auth.

debian/control
raddb/mods-available/mschap
src/modules/rlm_mschap/auth_wbclient.c [new file with mode: 0644]
src/modules/rlm_mschap/auth_wbclient.h [new file with mode: 0644]
src/modules/rlm_mschap/rlm_mschap.c
src/modules/rlm_mschap/rlm_mschap.h

index 129a508..ef8729e 100644 (file)
@@ -21,6 +21,7 @@ Build-Depends: debhelper (>= 7.4),
  libsqlite3-dev,
  libssl-dev,
  libtalloc-dev,
+ libwbclient-dev,
  libyubikey-dev,
  python-dev
 Section: net
index 2170df1..15ee91b 100644 (file)
@@ -66,6 +66,19 @@ mschap {
        #
 #      ntlm_auth_timeout = 10
 
+       # An alternative to using ntlm_auth is to connect to the
+       # winbind daemon directly for authentication. This option
+       # is likely to be faster and may be useful on busy systems,
+       # but is less well tested.
+       #
+       # Using this option requires libwbclient from Samba 4.2.1
+       # or later to be installed. Make sure that ntlm_auth above is
+       # commented out.
+       #
+#      winbind_username = "%{mschap:User-Name}"
+#      winbind_domain = "%{mschap:NT-Domain}"
+
+
        passchange {
                # This support MS-CHAPv2 (not v1) password change
                # requests.  See doc/mschap.rst for more IMPORTANT
diff --git a/src/modules/rlm_mschap/auth_wbclient.c b/src/modules/rlm_mschap/auth_wbclient.c
new file mode 100644 (file)
index 0000000..71b7ed6
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ *   This program is 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 2 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, write to the Free Software
+ *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * $Id$
+ * @file auth_wbclient.c
+ * @brief NTLM authentication against the wbclient library
+ *
+ * @copyright 2015  Matthew Newton
+ */
+
+RCSID("$Id$")
+
+#include <freeradius-devel/radiusd.h>
+#include <freeradius-devel/rad_assert.h>
+
+#include <wbclient.h>
+
+#include "rlm_mschap.h"
+#include "mschap.h"
+#include "auth_wbclient.h"
+
+#define NT_LENGTH 24
+
+/*
+ *     Check NTLM authentication direct to winbind via
+ *     Samba's libwbclient library
+ *
+ *     Returns -1 for failure and 0 on auth success
+ */
+int do_auth_wbclient(rlm_mschap_t *inst, REQUEST *request,
+                    uint8_t const *challenge, uint8_t const *response,
+                    uint8_t nthashhash[NT_DIGEST_LENGTH])
+{
+       int rcode = -1;
+       struct wbcAuthUserParams authparams;
+       wbcErr err;
+       int len;
+       struct wbcAuthUserInfo *info = NULL;
+       struct wbcAuthErrorInfo *error = NULL;
+       char user_name_buf[500];
+       char domain_name_buf[500];
+       uint8_t resp[NT_LENGTH];
+
+       /*
+        * Clear the auth parameters - this is important, as
+        * there are options that will cause wbcAuthenticateUserEx
+        * to bomb out if not zero.
+        */
+       memset(&authparams, 0, sizeof(authparams));
+
+       /*
+        * wb_username must be set for this function to be called
+        */
+       rad_assert(inst->wb_username);
+
+       /*
+        * Get the username and domain from the configuration
+        */
+       len = tmpl_expand(&authparams.account_name, user_name_buf, sizeof(user_name_buf),
+                         request, inst->wb_username, NULL, NULL);
+       if (len < 0) {
+               REDEBUG2("Unable to expand winbind_username");
+               goto done;
+       }
+
+       if (inst->wb_domain) {
+               len = tmpl_expand(&authparams.domain_name, domain_name_buf, sizeof(domain_name_buf),
+                                 request, inst->wb_domain, NULL, NULL);
+               if (len < 0) {
+                       REDEBUG2("Unable to expand winbind_domain");
+                       goto done;
+               }
+       } else {
+               RWDEBUG2("No domain specified; authentication may fail because of this");
+       }
+
+
+       /*
+        * Build the wbcAuthUserParams structure with what we know
+        */
+       authparams.level = WBC_AUTH_USER_LEVEL_RESPONSE;
+       authparams.password.response.nt_length = NT_LENGTH;
+
+       memcpy(resp, response, NT_LENGTH);
+       authparams.password.response.nt_data = resp;
+
+       memcpy(authparams.password.response.challenge, challenge,
+              sizeof(authparams.password.response.challenge));
+
+
+       /*
+        * Send auth request across to winbind
+        */
+       RDEBUG2("sending authentication request user='%s' domain='%s'", authparams.account_name,
+                                                                       authparams.domain_name);
+
+       err = wbcCtxAuthenticateUserEx(inst->wb_ctx, &authparams, &info, &error);
+
+
+       /*
+        * Try and give some useful feedback on what happened
+        */
+       switch (err) {
+       case WBC_ERR_SUCCESS:
+               rcode = 0;
+               RDEBUG2("Authenticated successfully");
+               /* Grab the nthashhash from the result */
+               memcpy(nthashhash, info->user_session_key, NT_DIGEST_LENGTH);
+               break;
+       case WBC_ERR_WINBIND_NOT_AVAILABLE:
+               RERROR("Unable to contact winbind!");
+               RERROR("Check that winbind is running and that FreeRADIUS");
+               RERROR("has permission to connect to the winbind socket.");
+               break;
+       case WBC_ERR_DOMAIN_NOT_FOUND:
+               REDEBUG2("Authentication failed: domain not found");
+               break;
+       case WBC_ERR_AUTH_ERROR:
+               REDEBUG2("Authentication failed (check domain is correct)");
+               break;
+       default:
+               REDEBUG2("Authentication failed: wbcErr %d", err);
+               if (error && error->display_string) {
+                       REDEBUG2("wbcErr %s", error->display_string);
+               }
+               break;
+       }
+
+
+done:
+       if (info) wbcFreeMemory(info);
+       if (error) wbcFreeMemory(error);
+
+       return rcode;
+}
+
diff --git a/src/modules/rlm_mschap/auth_wbclient.h b/src/modules/rlm_mschap/auth_wbclient.h
new file mode 100644 (file)
index 0000000..9ad5a2f
--- /dev/null
@@ -0,0 +1,12 @@
+/* Copyright 2015 The FreeRADIUS server project */
+
+#ifndef _AUTH_WBCLIENT_H
+#define _AUTH_WBCLIENT_H
+
+RCSIDH(auth_wbclient_h, "$Id$")
+
+int do_auth_wbclient(rlm_mschap_t *inst, REQUEST *request,
+                    uint8_t const *challenge, uint8_t const *response,
+                    uint8_t nthashhash[NT_DIGEST_LENGTH]);
+
+#endif /*_AUTH_WBCLIENT_H*/
index cb57604..b08b822 100644 (file)
@@ -36,6 +36,7 @@ RCSID("$Id$")
 #include       "rlm_mschap.h"
 #include       "mschap.h"
 #include       "smbdes.h"
+#include       "auth_wbclient.h"
 
 #ifdef HAVE_OPENSSL_CRYPTO_H
 USES_APPLE_DEPRECATED_API      /* OpenSSL API has been deprecated by Apple */
@@ -520,6 +521,8 @@ static const CONF_PARSER module_config[] = {
        { "passchange", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) passchange_config },
        { "allow_retry", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_mschap_t, allow_retry), "yes" },
        { "retry_msg", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_mschap_t, retry_msg), NULL },
+       { "winbind_username", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_TMPL, rlm_mschap_t, wb_username), NULL },
+       { "winbind_domain", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_TMPL, rlm_mschap_t, wb_domain), NULL },
 #ifdef WITH_OPEN_DIRECTORY
        { "use_open_directory", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_mschap_t, open_directory), "yes" },
 #endif
@@ -557,12 +560,40 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
        /*
         *      Set auth method
         */
+       inst->method = AUTH_INTERNAL;
+
+       if (inst->wb_username) {
+#ifdef WITH_AUTH_WINBIND
+               inst->method = AUTH_WBCLIENT;
+       
+               inst->wb_ctx = wbcCtxCreate();
+               if (!inst->wb_ctx) {
+                       ERROR("rlm_mschap (%s): failed to create winbind context", name);
+                       return -1;
+               }
+#else
+               ERROR("rlm_mschap (%s): 'winbind' auth not enabled at compiled time", name);
+               return -1;
+#endif
+       }
+
+       /* preserve existing behaviour: this option overrides all */
        if (inst->ntlm_auth) {
-               DEBUG("rlm_mschap (%s): authenticating by calling 'ntlm_auth'", name);
                inst->method = AUTH_NTLMAUTH_EXEC;
-       } else {
+       }
+
+       switch (inst->method) {
+       case AUTH_INTERNAL:
                DEBUG("rlm_mschap (%s): using internal authentication", name);
-               inst->method = AUTH_INTERNAL;
+               break;
+       case AUTH_NTLMAUTH_EXEC:
+               DEBUG("rlm_mschap (%s): authenticating by calling 'ntlm_auth'", name);
+               break;
+#ifdef WITH_AUTH_WINBIND
+       case AUTH_WBCLIENT:
+               DEBUG("rlm_mschap (%s): authenticating directly to winbind", name);
+               break;
+#endif
        }
 
        /*
@@ -586,6 +617,21 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
 }
 
 /*
+ *     Tidy up instance
+ */
+static int mod_detach(UNUSED void *instance)
+{
+#ifdef WITH_AUTH_WINBIND
+       rlm_mschap_t *inst = instance;
+
+       wbcCtxFree(inst->wb_ctx);
+#endif
+
+       return 0;
+}
+
+
+/*
  *     add_reply() adds either MS-CHAP2-Success or MS-CHAP-Error
  *     attribute to reply packet
  */
@@ -1118,6 +1164,13 @@ static int CC_HINT(nonnull (1, 2, 4, 5 ,6)) do_mschap(rlm_mschap_t *inst, REQUES
 
                break;
                }
+#ifdef WITH_AUTH_WINBIND
+       case AUTH_WBCLIENT:
+       /*
+        *      Process auth via the wbclient library
+        */
+               return do_auth_wbclient(inst, request, challenge, response, nthashhash);
+#endif
        default:
                /* We should never reach this line */
                RERROR("Internal error: Unknown mschap auth method (%d)", method);
@@ -1904,7 +1957,7 @@ module_t rlm_mschap = {
        sizeof(rlm_mschap_t),
        module_config,
        mod_instantiate,                /* instantiation */
-       NULL,                           /* detach */
+       mod_detach,                     /* detach */
        {
                mod_authenticate,       /* authenticate */
                mod_authorize,          /* authorize */
index 87eb6dd..14463e6 100644 (file)
@@ -5,10 +5,20 @@
 
 RCSIDH(rlm_mschap_h, "$Id$")
 
+#include "config.h"
+
+#ifdef HAVE_WBCLIENT_H
+#define WITH_AUTH_WINBIND
+#include <wbclient.h>
+#endif
+
 /* Method of authentication we are going to use */
 typedef enum {
        AUTH_INTERNAL           = 0,
        AUTH_NTLMAUTH_EXEC      = 1
+#ifdef WITH_AUTH_WINBIND
+       ,AUTH_WBCLIENT          = 2
+#endif
 } MSCHAP_AUTH_METHOD;
 
 typedef struct rlm_mschap_t {
@@ -27,6 +37,11 @@ typedef struct rlm_mschap_t {
        bool                    allow_retry;
        char const              *retry_msg;
        MSCHAP_AUTH_METHOD      method;
+       vp_tmpl_t               *wb_username;
+       vp_tmpl_t               *wb_domain;
+#ifdef WITH_AUTH_WINBIND
+       struct wbcContext       *wb_ctx;
+#endif
 #ifdef WITH_OPEN_DIRECTORY
        bool                    open_directory;
 #endif