libwbclient: wbcAuthenticateUserEx() be more strict regarding invalid parameters
[ira/wip.git] / source3 / nsswitch / libwbclient / wbc_pam.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    Winbind client API
5
6    Copyright (C) Gerald (Jerry) Carter 2007
7
8    This library is free software; you can redistribute it and/or
9    modify it under the terms of the GNU Lesser General Public
10    License as published by the Free Software Foundation; either
11    version 3 of the License, or (at your option) any later version.
12
13    This library is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16    Library General Public License for more details.
17
18    You should have received a copy of the GNU Lesser General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 /* Required Headers */
23
24 #include "libwbclient.h"
25
26 /** @brief Authenticate a username/password pair
27  *
28  * @param username     Name of user to authenticate
29  * @param password     Clear text password os user
30  *
31  * @return #wbcErr
32  **/
33
34 wbcErr wbcAuthenticateUser(const char *username,
35                            const char *password)
36 {
37         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
38         struct winbindd_request request;
39         struct winbindd_response response;
40
41         if (!username) {
42                 wbc_status = WBC_ERR_INVALID_PARAM;
43                 BAIL_ON_WBC_ERROR(wbc_status);
44         }
45
46         /* Initialize request */
47
48         ZERO_STRUCT(request);
49         ZERO_STRUCT(response);
50
51         /* dst is already null terminated from the memset above */
52
53         strncpy(request.data.auth.user, username,
54                 sizeof(request.data.auth.user)-1);
55         strncpy(request.data.auth.pass, password,
56                 sizeof(request.data.auth.user)-1);
57
58         wbc_status = wbcRequestResponse(WINBINDD_PAM_AUTH,
59                                         &request,
60                                         &response);
61         BAIL_ON_WBC_ERROR(wbc_status);
62
63 done:
64         return wbc_status;
65 }
66
67 static wbcErr wbc_create_auth_info(TALLOC_CTX *mem_ctx,
68                                    const struct winbindd_response *resp,
69                                    struct wbcAuthUserInfo **_i)
70 {
71         wbcErr wbc_status = WBC_ERR_SUCCESS;
72         struct wbcAuthUserInfo *i;
73         struct wbcDomainSid domain_sid;
74         char *p;
75         uint32_t sn = 0;
76         uint32_t j;
77
78         i = talloc(mem_ctx, struct wbcAuthUserInfo);
79         BAIL_ON_PTR_ERROR(i, wbc_status);
80
81         i->user_flags   = resp->data.auth.info3.user_flgs;
82
83         i->account_name = talloc_strdup(i, resp->data.auth.info3.user_name);
84         BAIL_ON_PTR_ERROR(i->account_name, wbc_status);
85         i->user_principal= NULL;
86         i->full_name    = talloc_strdup(i, resp->data.auth.info3.full_name);
87         BAIL_ON_PTR_ERROR(i->full_name, wbc_status);
88         i->domain_name  = talloc_strdup(i, resp->data.auth.info3.logon_dom);
89         BAIL_ON_PTR_ERROR(i->domain_name, wbc_status);
90         i->dns_domain_name= NULL;
91
92         i->acct_flags   = resp->data.auth.info3.acct_flags;
93         memcpy(i->user_session_key,
94                resp->data.auth.user_session_key,
95                sizeof(i->user_session_key));
96         memcpy(i->lm_session_key,
97                resp->data.auth.first_8_lm_hash,
98                sizeof(i->lm_session_key));
99
100         i->logon_count          = resp->data.auth.info3.logon_count;
101         i->bad_password_count   = resp->data.auth.info3.bad_pw_count;
102
103         i->logon_time           = resp->data.auth.info3.logon_time;
104         i->logoff_time          = resp->data.auth.info3.logoff_time;
105         i->kickoff_time         = resp->data.auth.info3.kickoff_time;
106         i->pass_last_set_time   = resp->data.auth.info3.pass_last_set_time;
107         i->pass_can_change_time = resp->data.auth.info3.pass_can_change_time;
108         i->pass_must_change_time= resp->data.auth.info3.pass_must_change_time;
109
110         i->logon_server = talloc_strdup(i, resp->data.auth.info3.logon_srv);
111         BAIL_ON_PTR_ERROR(i->logon_server, wbc_status);
112         i->logon_script = talloc_strdup(i, resp->data.auth.info3.logon_script);
113         BAIL_ON_PTR_ERROR(i->logon_script, wbc_status);
114         i->profile_path = talloc_strdup(i, resp->data.auth.info3.profile_path);
115         BAIL_ON_PTR_ERROR(i->profile_path, wbc_status);
116         i->home_directory= talloc_strdup(i, resp->data.auth.info3.home_dir);
117         BAIL_ON_PTR_ERROR(i->home_directory, wbc_status);
118         i->home_drive   = talloc_strdup(i, resp->data.auth.info3.dir_drive);
119         BAIL_ON_PTR_ERROR(i->home_drive, wbc_status);
120
121         i->num_sids     = 2;
122         i->num_sids     += resp->data.auth.info3.num_groups;
123         i->num_sids     += resp->data.auth.info3.num_other_sids;
124
125         i->sids = talloc_array(i, struct wbcSidWithAttr, i->num_sids);
126         BAIL_ON_PTR_ERROR(i->sids, wbc_status);
127
128         wbc_status = wbcStringToSid(resp->data.auth.info3.dom_sid,
129                                     &domain_sid);
130         BAIL_ON_WBC_ERROR(wbc_status);
131
132 #define _SID_COMPOSE(s, d, r, a) { \
133         (s).sid = d; \
134         if ((s).sid.num_auths < MAXSUBAUTHS) { \
135                 (s).sid.sub_auths[(s).sid.num_auths++] = r; \
136         } else { \
137                 wbc_status = WBC_ERR_INVALID_SID; \
138                 BAIL_ON_WBC_ERROR(wbc_status); \
139         } \
140         (s).attributes = a; \
141 } while (0)
142
143         sn = 0;
144         _SID_COMPOSE(i->sids[sn], domain_sid,
145                      resp->data.auth.info3.user_rid,
146                      0);
147         sn++;
148         _SID_COMPOSE(i->sids[sn], domain_sid,
149                      resp->data.auth.info3.group_rid,
150                      0);
151         sn++;
152
153         p = resp->extra_data.data;
154         if (!p) {
155                 wbc_status = WBC_INVALID_RESPONSE;
156                 BAIL_ON_WBC_ERROR(wbc_status);
157         }
158
159         for (j=0; j < resp->data.auth.info3.num_groups; j++) {
160                 uint32_t rid;
161                 uint32_t attrs;
162                 int ret;
163                 char *s = p;
164                 char *e = strchr(p, '\n');
165                 if (!e) {
166                         wbc_status = WBC_INVALID_RESPONSE;
167                         BAIL_ON_WBC_ERROR(wbc_status);
168                 }
169                 e[0] = '\0';
170                 p = &e[1];
171
172                 ret = sscanf(s, "0x%08X:0x%08X", &rid, &attrs);
173                 if (ret != 2) {
174                         wbc_status = WBC_INVALID_RESPONSE;
175                         BAIL_ON_WBC_ERROR(wbc_status);
176                 }
177
178                 _SID_COMPOSE(i->sids[sn], domain_sid,
179                              rid, attrs);
180                 sn++;
181         }
182
183         for (j=0; j < resp->data.auth.info3.num_other_sids; j++) {
184                 uint32_t attrs;
185                 int ret;
186                 char *s = p;
187                 char *a;
188                 char *e = strchr(p, '\n');
189                 if (!e) {
190                         wbc_status = WBC_INVALID_RESPONSE;
191                         BAIL_ON_WBC_ERROR(wbc_status);
192                 }
193                 e[0] = '\0';
194                 p = &e[1];
195
196                 e = strchr(s, ':');
197                 if (!e) {
198                         wbc_status = WBC_INVALID_RESPONSE;
199                         BAIL_ON_WBC_ERROR(wbc_status);
200                 }
201                 e[0] = '\0';
202                 a = &e[1];
203
204                 ret = sscanf(a, "0x%08X",
205                              &attrs);
206                 if (ret != 1) {
207                         wbc_status = WBC_INVALID_RESPONSE;
208                         BAIL_ON_WBC_ERROR(wbc_status);
209                 }
210
211                 wbc_status = wbcStringToSid(s, &i->sids[sn].sid);
212                 BAIL_ON_WBC_ERROR(wbc_status);
213
214                 i->sids[sn].attributes = attrs;
215                 sn++;
216         }
217
218         i->num_sids = sn;
219
220         *_i = i;
221         i = NULL;
222 done:
223         talloc_free(i);
224         return wbc_status;
225 }
226
227 static wbcErr wbc_create_error_info(TALLOC_CTX *mem_ctx,
228                                   const struct winbindd_response *resp,
229                                   struct wbcAuthErrorInfo **_e)
230 {
231         wbcErr wbc_status = WBC_ERR_SUCCESS;
232         struct wbcAuthErrorInfo *e;
233
234         e = talloc(mem_ctx, struct wbcAuthErrorInfo);
235         BAIL_ON_PTR_ERROR(e, wbc_status);
236
237         e->nt_status = resp->data.auth.nt_status;
238         e->pam_error = resp->data.auth.pam_error;
239         e->nt_string = talloc_strdup(e, resp->data.auth.nt_status_string);
240         BAIL_ON_PTR_ERROR(e->nt_string, wbc_status);
241
242         e->display_string = talloc_strdup(e, resp->data.auth.error_string);
243         BAIL_ON_PTR_ERROR(e->display_string, wbc_status);
244
245         *_e = e;
246         e = NULL;
247
248 done:
249         talloc_free(e);
250         return wbc_status;
251 }
252
253 /** @brief Authenticate with more detailed information
254  *
255  * @param params       Input parameters, only WBC_AUTH_USER_LEVEL_RESPONSE
256  *                     is supported yet
257  * @param info         Output details on WBC_ERR_SUCCESS
258  * @param error        Output details on WBC_ERR_AUTH_ERROR
259  *
260  * @return #wbcErr
261  **/
262
263 wbcErr wbcAuthenticateUserEx(const struct wbcAuthUserParams *params,
264                              struct wbcAuthUserInfo **info,
265                              struct wbcAuthErrorInfo **error)
266 {
267         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
268         int cmd = 0;
269         struct winbindd_request request;
270         struct winbindd_response response;
271
272         ZERO_STRUCT(request);
273         ZERO_STRUCT(response);
274
275         if (error) {
276                 *error = NULL;
277         }
278
279         if (!params) {
280                 wbc_status = WBC_ERR_INVALID_PARAM;
281                 BAIL_ON_WBC_ERROR(wbc_status);
282         }
283
284         if (!params->account_name) {
285                 wbc_status = WBC_ERR_INVALID_PARAM;
286                 BAIL_ON_WBC_ERROR(wbc_status);
287         }
288
289         /* Initialize request */
290
291         switch (params->level) {
292         case WBC_AUTH_USER_LEVEL_PLAIN:
293                 wbc_status = WBC_ERR_NOT_IMPLEMENTED;
294                 BAIL_ON_WBC_ERROR(wbc_status);
295                 break;
296
297         case WBC_AUTH_USER_LEVEL_HASH:
298                 wbc_status = WBC_ERR_NOT_IMPLEMENTED;
299                 BAIL_ON_WBC_ERROR(wbc_status);
300                 break;
301
302         case WBC_AUTH_USER_LEVEL_RESPONSE:
303                 cmd = WINBINDD_PAM_AUTH_CRAP;
304                 request.flags = WBFLAG_PAM_INFO3_TEXT |
305                                 WBFLAG_PAM_USER_SESSION_KEY |
306                                 WBFLAG_PAM_LMKEY;
307
308                 if (params->password.response.lm_length &&
309                     params->password.response.lm_data) {
310                         wbc_status = WBC_ERR_INVALID_PARAM;
311                         BAIL_ON_WBC_ERROR(wbc_status);
312                 }
313                 if (params->password.response.lm_length == 0 &&
314                     params->password.response.lm_data) {
315                         wbc_status = WBC_ERR_INVALID_PARAM;
316                         BAIL_ON_WBC_ERROR(wbc_status);
317                 }
318
319                 if (params->password.response.nt_length &&
320                     !params->password.response.nt_data) {
321                         wbc_status = WBC_ERR_INVALID_PARAM;
322                         BAIL_ON_WBC_ERROR(wbc_status);
323                 }
324                 if (params->password.response.nt_length == 0&&
325                     params->password.response.nt_data) {
326                         wbc_status = WBC_ERR_INVALID_PARAM;
327                         BAIL_ON_WBC_ERROR(wbc_status);
328                 }
329
330                 strncpy(request.data.auth_crap.user,
331                         params->account_name,
332                         sizeof(request.data.auth_crap.user)-1);
333                 if (params->domain_name) {
334                         strncpy(request.data.auth_crap.domain,
335                                 params->domain_name,
336                                 sizeof(request.data.auth_crap.domain)-1);
337                 }
338                 if (params->workstation_name) {
339                         strncpy(request.data.auth_crap.workstation,
340                                 params->workstation_name,
341                                 sizeof(request.data.auth_crap.workstation)-1);
342                 }
343
344                 request.data.auth_crap.logon_parameters =
345                                 params->parameter_control;
346
347                 memcpy(request.data.auth_crap.chal,
348                        params->password.response.challenge,
349                        sizeof(request.data.auth_crap.chal));
350
351                 request.data.auth_crap.lm_resp_len =
352                                 MIN(params->password.response.lm_length,
353                                     sizeof(request.data.auth_crap.lm_resp));
354                 request.data.auth_crap.nt_resp_len =
355                                 MIN(params->password.response.nt_length,
356                                     sizeof(request.data.auth_crap.nt_resp));
357                 if (params->password.response.lm_data) {
358                         memcpy(request.data.auth_crap.lm_resp,
359                                params->password.response.lm_data,
360                                request.data.auth_crap.lm_resp_len);
361                 }
362                 if (params->password.response.nt_data) {
363                         memcpy(request.data.auth_crap.nt_resp,
364                                params->password.response.nt_data,
365                                request.data.auth_crap.nt_resp_len);
366                 }
367                 break;
368         }
369
370         if (cmd == 0) {
371                 wbc_status = WBC_ERR_INVALID_PARAM;
372                 BAIL_ON_WBC_ERROR(wbc_status);
373         }
374
375         wbc_status = wbcRequestResponse(cmd,
376                                         &request,
377                                         &response);
378         if (response.data.auth.nt_status != 0) {
379                 if (error) {
380                         wbc_status = wbc_create_error_info(NULL,
381                                                            &response,
382                                                            error);
383                         BAIL_ON_WBC_ERROR(wbc_status);
384                 }
385
386                 wbc_status = WBC_ERR_AUTH_ERROR;
387                 BAIL_ON_WBC_ERROR(wbc_status);
388         }
389         BAIL_ON_WBC_ERROR(wbc_status);
390
391         if (info) {
392                 wbc_status = wbc_create_auth_info(NULL,
393                                                   &response,
394                                                   info);
395                 BAIL_ON_WBC_ERROR(wbc_status);
396         }
397
398 done:
399
400         return wbc_status;
401 }