Merge tag 'release_3_0_12' into branch moonshot-fr-3.0.12-upgrade.
[freeradius.git] / src / modules / rlm_mschap / auth_wbclient.c
1 /*
2  *   This program is is free software; you can redistribute it and/or modify
3  *   it under the terms of the GNU General Public License as published by
4  *   the Free Software Foundation; either version 2 of the License, or (at
5  *   your option) any later version.
6  *
7  *   This program is distributed in the hope that it will be useful,
8  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
9  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  *   GNU General Public License for more details.
11  *
12  *   You should have received a copy of the GNU General Public License
13  *   along with this program; if not, write to the Free Software
14  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15  */
16
17 /**
18  * $Id$
19  * @file auth_wbclient.c
20  * @brief NTLM authentication against the wbclient library
21  *
22  * @copyright 2015  Matthew Newton
23  */
24
25 RCSID("$Id$")
26
27 #include <freeradius-devel/radiusd.h>
28 #include <freeradius-devel/rad_assert.h>
29
30 #include <core/ntstatus.h>
31
32 #include "rlm_mschap.h"
33 #include "mschap.h"
34 #include "auth_wbclient.h"
35
36 #define NT_LENGTH 24
37
38 /*
39  *      Check NTLM authentication direct to winbind via
40  *      Samba's libwbclient library
41  *
42  *      Returns:
43  *       0    success
44  *       -1   auth failure
45  *       -648 password expired
46  */
47 int do_auth_wbclient(rlm_mschap_t *inst, REQUEST *request,
48                      uint8_t const *challenge, uint8_t const *response,
49                      uint8_t nthashhash[NT_DIGEST_LENGTH])
50 {
51         int rcode = -1;
52         struct wbcContext *wb_ctx;
53         struct wbcAuthUserParams authparams;
54         wbcErr err;
55         int len;
56         struct wbcAuthUserInfo *info = NULL;
57         struct wbcAuthErrorInfo *error = NULL;
58         char user_name_buf[500];
59         char domain_name_buf[500];
60         uint8_t resp[NT_LENGTH];
61
62         /*
63          * Clear the auth parameters - this is important, as
64          * there are options that will cause wbcAuthenticateUserEx
65          * to bomb out if not zero.
66          */
67         memset(&authparams, 0, sizeof(authparams));
68
69         /*
70          * wb_username must be set for this function to be called
71          */
72         rad_assert(inst->wb_username);
73
74         /*
75          * Get the username and domain from the configuration
76          */
77         len = tmpl_expand(&authparams.account_name, user_name_buf, sizeof(user_name_buf),
78                           request, inst->wb_username, NULL, NULL);
79         if (len < 0) {
80                 REDEBUG2("Unable to expand winbind_username");
81                 goto done;
82         }
83
84         if (inst->wb_domain) {
85                 len = tmpl_expand(&authparams.domain_name, domain_name_buf, sizeof(domain_name_buf),
86                                   request, inst->wb_domain, NULL, NULL);
87                 if (len < 0) {
88                         REDEBUG2("Unable to expand winbind_domain");
89                         goto done;
90                 }
91         } else {
92                 RWDEBUG2("No domain specified; authentication may fail because of this");
93         }
94
95
96         /*
97          * Build the wbcAuthUserParams structure with what we know
98          */
99         authparams.level = WBC_AUTH_USER_LEVEL_RESPONSE;
100         authparams.password.response.nt_length = NT_LENGTH;
101
102         memcpy(resp, response, NT_LENGTH);
103         authparams.password.response.nt_data = resp;
104
105         memcpy(authparams.password.response.challenge, challenge,
106                sizeof(authparams.password.response.challenge));
107
108         authparams.parameter_control |= WBC_MSV1_0_ALLOW_MSVCHAPV2 |
109                                         WBC_MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT |
110                                         WBC_MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT;
111
112
113         /*
114          * Send auth request across to winbind
115          */
116         wb_ctx = fr_connection_get(inst->wb_pool);
117         if (wb_ctx == NULL) {
118                 RERROR("Unable to get winbind connection from pool");
119                 goto done;
120         }
121
122         RDEBUG2("sending authentication request user='%s' domain='%s'", authparams.account_name,
123                                                                         authparams.domain_name);
124
125         err = wbcCtxAuthenticateUserEx(wb_ctx, &authparams, &info, &error);
126
127         fr_connection_release(inst->wb_pool, wb_ctx);
128
129
130         /*
131          * Try and give some useful feedback on what happened. There are only
132          * a few errors that can actually be returned from wbcCtxAuthenticateUserEx.
133          */
134         switch (err) {
135         case WBC_ERR_SUCCESS:
136                 rcode = 0;
137                 RDEBUG2("Authenticated successfully");
138                 /* Grab the nthashhash from the result */
139                 memcpy(nthashhash, info->user_session_key, NT_DIGEST_LENGTH);
140                 break;
141         case WBC_ERR_WINBIND_NOT_AVAILABLE:
142                 RERROR("Unable to contact winbind!");
143                 RDEBUG2("Check that winbind is running and that FreeRADIUS has");
144                 RDEBUG2("permission to connect to the winbind privileged socket.");
145                 break;
146         case WBC_ERR_DOMAIN_NOT_FOUND:
147                 REDEBUG2("Domain not found");
148                 break;
149         case WBC_ERR_AUTH_ERROR:
150                 if (!error) {
151                         REDEBUG2("Authentication failed");
152                         break;
153                 }
154
155                 /*
156                  * The password needs to be changed, so set rcode appropriately.
157                  */
158                 if (error->nt_status & NT_STATUS_PASSWORD_EXPIRED ||
159                     error->nt_status & NT_STATUS_PASSWORD_MUST_CHANGE) {
160                         rcode = -648;
161                 }
162
163                 /*
164                  * Return the NT_STATUS human readable error string, if there is one.
165                  */
166                 if (error->display_string) {
167                         REDEBUG2("%s [0x%X]", error->display_string, error->nt_status);
168                 } else {
169                         REDEBUG2("Authentication failed [0x%X]", error->nt_status);
170                 }
171                 break;
172         default:
173                 /*
174                  * Only errors left are 
175                  *   WBC_ERR_INVALID_PARAM
176                  *   WBC_ERR_NO_MEMORY
177                  * neither of which are particularly likely.
178                  */
179                 if (error && error->display_string) {
180                         REDEBUG2("libwbclient error: wbcErr %d (%s)", err, error->display_string);
181                 } else {
182                         REDEBUG2("libwbclient error: wbcErr %d", err);
183                 }
184                 break;
185         }
186
187
188 done:
189         if (info) wbcFreeMemory(info);
190         if (error) wbcFreeMemory(error);
191
192         return rcode;
193 }
194