winbindd: winbindd_ccache_ntlm_auth() -> bool_dispatch_table
[samba.git] / 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    Copyright (C) Guenther Deschner 2008
8    Copyright (C) Volker Lendecke 2009
9
10    This library is free software; you can redistribute it and/or
11    modify it under the terms of the GNU Lesser General Public
12    License as published by the Free Software Foundation; either
13    version 3 of the License, or (at your option) any later version.
14
15    This library is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18    Library General Public License for more details.
19
20    You should have received a copy of the GNU Lesser General Public License
21    along with this program.  If not, see <http://www.gnu.org/licenses/>.
22 */
23
24 /* Required Headers */
25
26 #include "replace.h"
27 #include "libwbclient.h"
28 #include "../winbind_client.h"
29
30 /* Authenticate a username/password pair */
31 wbcErr wbcCtxAuthenticateUser(struct wbcContext *ctx,
32                               const char *username, const char *password)
33 {
34         wbcErr wbc_status = WBC_ERR_SUCCESS;
35         struct wbcAuthUserParams params;
36
37         ZERO_STRUCT(params);
38
39         params.account_name             = username;
40         params.level                    = WBC_AUTH_USER_LEVEL_PLAIN;
41         params.password.plaintext       = password;
42
43         wbc_status = wbcCtxAuthenticateUserEx(ctx, &params, NULL, NULL);
44         BAIL_ON_WBC_ERROR(wbc_status);
45
46 done:
47         return wbc_status;
48 }
49
50 wbcErr wbcAuthenticateUser(const char *username, const char *password)
51 {
52         return wbcCtxAuthenticateUser(NULL, username, password);
53 }
54
55 static bool sid_attr_compose(struct wbcSidWithAttr *s,
56                              const struct wbcDomainSid *d,
57                              uint32_t rid, uint32_t attr)
58 {
59         if (d->num_auths >= WBC_MAXSUBAUTHS) {
60                 return false;
61         }
62         s->sid = *d;
63         s->sid.sub_auths[s->sid.num_auths++] = rid;
64         s->attributes = attr;
65         return true;
66 }
67
68 static void wbcAuthUserInfoDestructor(void *ptr)
69 {
70         struct wbcAuthUserInfo *i = (struct wbcAuthUserInfo *)ptr;
71         free(i->account_name);
72         free(i->user_principal);
73         free(i->full_name);
74         free(i->domain_name);
75         free(i->dns_domain_name);
76         free(i->logon_server);
77         free(i->logon_script);
78         free(i->profile_path);
79         free(i->home_directory);
80         free(i->home_drive);
81         free(i->sids);
82 }
83
84 static wbcErr wbc_create_auth_info(const struct winbindd_response *resp,
85                                    struct wbcAuthUserInfo **_i)
86 {
87         wbcErr wbc_status = WBC_ERR_SUCCESS;
88         struct wbcAuthUserInfo *i;
89         struct wbcDomainSid domain_sid;
90         char *p;
91         uint32_t sn = 0;
92         uint32_t j;
93
94         i = (struct wbcAuthUserInfo *)wbcAllocateMemory(
95                 1, sizeof(struct wbcAuthUserInfo),
96                 wbcAuthUserInfoDestructor);
97         BAIL_ON_PTR_ERROR(i, wbc_status);
98
99         i->user_flags   = resp->data.auth.info3.user_flgs;
100
101         i->account_name = strdup(resp->data.auth.info3.user_name);
102         BAIL_ON_PTR_ERROR(i->account_name, wbc_status);
103         if (resp->data.auth.validation_level == 6) {
104                 i->user_principal = strdup(resp->data.auth.info6.principal_name);
105                 BAIL_ON_PTR_ERROR(i->user_principal, wbc_status);
106         } else {
107                 i->user_principal = NULL;
108         }
109         i->full_name    = strdup(resp->data.auth.info3.full_name);
110         BAIL_ON_PTR_ERROR(i->full_name, wbc_status);
111         i->domain_name  = strdup(resp->data.auth.info3.logon_dom);
112         BAIL_ON_PTR_ERROR(i->domain_name, wbc_status);
113         if (resp->data.auth.validation_level == 6) {
114                 i->dns_domain_name = strdup(resp->data.auth.info6.dns_domainname);
115                 BAIL_ON_PTR_ERROR(i->dns_domain_name, wbc_status);
116         } else {
117                 i->dns_domain_name = NULL;
118         }
119
120         i->acct_flags   = resp->data.auth.info3.acct_flags;
121         memcpy(i->user_session_key,
122                resp->data.auth.user_session_key,
123                sizeof(i->user_session_key));
124         memcpy(i->lm_session_key,
125                resp->data.auth.first_8_lm_hash,
126                sizeof(i->lm_session_key));
127
128         i->logon_count          = resp->data.auth.info3.logon_count;
129         i->bad_password_count   = resp->data.auth.info3.bad_pw_count;
130
131         i->logon_time           = resp->data.auth.info3.logon_time;
132         i->logoff_time          = resp->data.auth.info3.logoff_time;
133         i->kickoff_time         = resp->data.auth.info3.kickoff_time;
134         i->pass_last_set_time   = resp->data.auth.info3.pass_last_set_time;
135         i->pass_can_change_time = resp->data.auth.info3.pass_can_change_time;
136         i->pass_must_change_time= resp->data.auth.info3.pass_must_change_time;
137
138         i->logon_server = strdup(resp->data.auth.info3.logon_srv);
139         BAIL_ON_PTR_ERROR(i->logon_server, wbc_status);
140         i->logon_script = strdup(resp->data.auth.info3.logon_script);
141         BAIL_ON_PTR_ERROR(i->logon_script, wbc_status);
142         i->profile_path = strdup(resp->data.auth.info3.profile_path);
143         BAIL_ON_PTR_ERROR(i->profile_path, wbc_status);
144         i->home_directory= strdup(resp->data.auth.info3.home_dir);
145         BAIL_ON_PTR_ERROR(i->home_directory, wbc_status);
146         i->home_drive   = strdup(resp->data.auth.info3.dir_drive);
147         BAIL_ON_PTR_ERROR(i->home_drive, wbc_status);
148
149         i->num_sids     = 2;
150         i->num_sids     += resp->data.auth.info3.num_groups;
151         i->num_sids     += resp->data.auth.info3.num_other_sids;
152
153         i->sids = (struct wbcSidWithAttr *)calloc(
154                 sizeof(struct wbcSidWithAttr), i->num_sids);
155         BAIL_ON_PTR_ERROR(i->sids, wbc_status);
156
157         wbc_status = wbcStringToSid(resp->data.auth.info3.dom_sid,
158                                     &domain_sid);
159         BAIL_ON_WBC_ERROR(wbc_status);
160
161         sn = 0;
162         if (!sid_attr_compose(&i->sids[sn], &domain_sid,
163                               resp->data.auth.info3.user_rid, 0)) {
164                 wbc_status = WBC_ERR_INVALID_SID;
165                 goto done;
166         }
167         sn++;
168         if (!sid_attr_compose(&i->sids[sn], &domain_sid,
169                               resp->data.auth.info3.group_rid, 0)) {
170                 wbc_status = WBC_ERR_INVALID_SID;
171                 goto done;
172         }
173         sn++;
174
175         p = (char *)resp->extra_data.data;
176         if (!p) {
177                 wbc_status = WBC_ERR_INVALID_RESPONSE;
178                 BAIL_ON_WBC_ERROR(wbc_status);
179         }
180
181         for (j=0; j < resp->data.auth.info3.num_groups; j++) {
182                 uint32_t rid;
183                 uint32_t attrs;
184                 int ret;
185                 char *s = p;
186                 char *e = strchr(p, '\n');
187                 if (!e) {
188                         wbc_status = WBC_ERR_INVALID_RESPONSE;
189                         BAIL_ON_WBC_ERROR(wbc_status);
190                 }
191                 e[0] = '\0';
192                 p = &e[1];
193
194                 ret = sscanf(s, "0x%08X:0x%08X", &rid, &attrs);
195                 if (ret != 2) {
196                         wbc_status = WBC_ERR_INVALID_RESPONSE;
197                         BAIL_ON_WBC_ERROR(wbc_status);
198                 }
199
200                 if (!sid_attr_compose(&i->sids[sn], &domain_sid,
201                                       rid, attrs)) {
202                         wbc_status = WBC_ERR_INVALID_SID;
203                         goto done;
204                 }
205                 sn++;
206         }
207
208         for (j=0; j < resp->data.auth.info3.num_other_sids; j++) {
209                 uint32_t attrs;
210                 int ret;
211                 char *s = p;
212                 char *a;
213                 char *e = strchr(p, '\n');
214                 if (!e) {
215                         wbc_status = WBC_ERR_INVALID_RESPONSE;
216                         BAIL_ON_WBC_ERROR(wbc_status);
217                 }
218                 e[0] = '\0';
219                 p = &e[1];
220
221                 e = strchr(s, ':');
222                 if (!e) {
223                         wbc_status = WBC_ERR_INVALID_RESPONSE;
224                         BAIL_ON_WBC_ERROR(wbc_status);
225                 }
226                 e[0] = '\0';
227                 a = &e[1];
228
229                 ret = sscanf(a, "0x%08X",
230                              &attrs);
231                 if (ret != 1) {
232                         wbc_status = WBC_ERR_INVALID_RESPONSE;
233                         BAIL_ON_WBC_ERROR(wbc_status);
234                 }
235
236                 wbc_status = wbcStringToSid(s, &i->sids[sn].sid);
237                 BAIL_ON_WBC_ERROR(wbc_status);
238
239                 i->sids[sn].attributes = attrs;
240                 sn++;
241         }
242
243         i->num_sids = sn;
244
245         *_i = i;
246         i = NULL;
247 done:
248         wbcFreeMemory(i);
249         return wbc_status;
250 }
251
252 static void wbcAuthErrorInfoDestructor(void *ptr)
253 {
254         struct wbcAuthErrorInfo *e = (struct wbcAuthErrorInfo *)ptr;
255         free(e->nt_string);
256         free(e->display_string);
257 }
258
259 static wbcErr wbc_create_error_info(const struct winbindd_response *resp,
260                                     struct wbcAuthErrorInfo **_e)
261 {
262         wbcErr wbc_status = WBC_ERR_SUCCESS;
263         struct wbcAuthErrorInfo *e;
264
265         e = (struct wbcAuthErrorInfo *)wbcAllocateMemory(
266                 1, sizeof(struct wbcAuthErrorInfo),
267                 wbcAuthErrorInfoDestructor);
268         BAIL_ON_PTR_ERROR(e, wbc_status);
269
270         e->nt_status = resp->data.auth.nt_status;
271         e->pam_error = resp->data.auth.pam_error;
272         e->authoritative = resp->data.auth.authoritative;
273         e->nt_string = strdup(resp->data.auth.nt_status_string);
274         BAIL_ON_PTR_ERROR(e->nt_string, wbc_status);
275
276         e->display_string = strdup(resp->data.auth.error_string);
277         BAIL_ON_PTR_ERROR(e->display_string, wbc_status);
278
279         *_e = e;
280         e = NULL;
281
282 done:
283         wbcFreeMemory(e);
284         return wbc_status;
285 }
286
287 static wbcErr wbc_create_password_policy_info(const struct winbindd_response *resp,
288                                               struct wbcUserPasswordPolicyInfo **_i)
289 {
290         wbcErr wbc_status = WBC_ERR_SUCCESS;
291         struct wbcUserPasswordPolicyInfo *i;
292
293         i = (struct wbcUserPasswordPolicyInfo *)wbcAllocateMemory(
294                 1, sizeof(struct wbcUserPasswordPolicyInfo), NULL);
295         BAIL_ON_PTR_ERROR(i, wbc_status);
296
297         i->min_passwordage      = resp->data.auth.policy.min_passwordage;
298         i->min_length_password  = resp->data.auth.policy.min_length_password;
299         i->password_history     = resp->data.auth.policy.password_history;
300         i->password_properties  = resp->data.auth.policy.password_properties;
301         i->expire               = resp->data.auth.policy.expire;
302
303         *_i = i;
304         i = NULL;
305
306 done:
307         wbcFreeMemory(i);
308         return wbc_status;
309 }
310
311 static void wbcLogonUserInfoDestructor(void *ptr)
312 {
313         struct wbcLogonUserInfo *i = (struct wbcLogonUserInfo *)ptr;
314         wbcFreeMemory(i->info);
315         wbcFreeMemory(i->blobs);
316 }
317
318 static wbcErr wbc_create_logon_info(struct winbindd_response *resp,
319                                     struct wbcLogonUserInfo **_i)
320 {
321         wbcErr wbc_status = WBC_ERR_SUCCESS;
322         struct wbcLogonUserInfo *i;
323
324         i = (struct wbcLogonUserInfo *)wbcAllocateMemory(
325                 1, sizeof(struct wbcLogonUserInfo),
326                 wbcLogonUserInfoDestructor);
327         BAIL_ON_PTR_ERROR(i, wbc_status);
328
329         wbc_status = wbc_create_auth_info(resp, &i->info);
330         BAIL_ON_WBC_ERROR(wbc_status);
331
332         if (resp->data.auth.krb5ccname[0] != '\0') {
333                 wbc_status = wbcAddNamedBlob(&i->num_blobs,
334                                              &i->blobs,
335                                              "krb5ccname",
336                                              0,
337                                              (uint8_t *)resp->data.auth.krb5ccname,
338                                              strlen(resp->data.auth.krb5ccname)+1);
339                 BAIL_ON_WBC_ERROR(wbc_status);
340         }
341
342         if (resp->data.auth.unix_username[0] != '\0') {
343                 wbc_status = wbcAddNamedBlob(&i->num_blobs,
344                                              &i->blobs,
345                                              "unix_username",
346                                              0,
347                                              (uint8_t *)resp->data.auth.unix_username,
348                                              strlen(resp->data.auth.unix_username)+1);
349                 BAIL_ON_WBC_ERROR(wbc_status);
350         }
351
352         *_i = i;
353         i = NULL;
354 done:
355         wbcFreeMemory(i);
356         return wbc_status;
357 }
358
359
360 /* Authenticate with more detailed information */
361 wbcErr wbcCtxAuthenticateUserEx(struct wbcContext *ctx,
362                                 const struct wbcAuthUserParams *params,
363                                 struct wbcAuthUserInfo **info,
364                                 struct wbcAuthErrorInfo **error)
365 {
366         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
367         int cmd = 0;
368         struct winbindd_request request;
369         struct winbindd_response response;
370
371         ZERO_STRUCT(request);
372         ZERO_STRUCT(response);
373
374         if (error) {
375                 *error = NULL;
376         }
377
378         if (!params) {
379                 wbc_status = WBC_ERR_INVALID_PARAM;
380                 BAIL_ON_WBC_ERROR(wbc_status);
381         }
382
383         if (params->level != WBC_AUTH_USER_LEVEL_PAC && !params->account_name) {
384                 wbc_status = WBC_ERR_INVALID_PARAM;
385                 BAIL_ON_WBC_ERROR(wbc_status);
386         }
387
388         /* Initialize request */
389
390         switch (params->level) {
391         case WBC_AUTH_USER_LEVEL_PLAIN:
392                 cmd = WINBINDD_PAM_AUTH;
393                 request.flags = WBFLAG_PAM_INFO3_TEXT |
394                                 WBFLAG_PAM_USER_SESSION_KEY |
395                                 WBFLAG_PAM_LMKEY;
396
397                 if (!params->password.plaintext) {
398                         wbc_status = WBC_ERR_INVALID_PARAM;
399                         BAIL_ON_WBC_ERROR(wbc_status);
400                 }
401
402                 if (params->domain_name && params->domain_name[0]) {
403                         /* We need to get the winbind separator :-( */
404                         struct winbindd_response sep_response;
405
406                         ZERO_STRUCT(sep_response);
407
408                         wbc_status = wbcRequestResponse(ctx, WINBINDD_INFO,
409                                                         NULL, &sep_response);
410                         BAIL_ON_WBC_ERROR(wbc_status);
411
412                         snprintf(request.data.auth.user,
413                                  sizeof(request.data.auth.user)-1,
414                                  "%s%c%s",
415                                  params->domain_name,
416                                  sep_response.data.info.winbind_separator,
417                                  params->account_name);
418                 } else {
419                         strncpy(request.data.auth.user,
420                                 params->account_name,
421                                 sizeof(request.data.auth.user)-1);
422                 }
423
424                 strncpy(request.data.auth.pass,
425                         params->password.plaintext,
426                         sizeof(request.data.auth.pass)-1);
427                 break;
428
429         case WBC_AUTH_USER_LEVEL_HASH:
430                 wbc_status = WBC_ERR_NOT_IMPLEMENTED;
431                 BAIL_ON_WBC_ERROR(wbc_status);
432                 break;
433
434         case WBC_AUTH_USER_LEVEL_RESPONSE:
435                 cmd = WINBINDD_PAM_AUTH_CRAP;
436                 request.flags = WBFLAG_PAM_INFO3_TEXT |
437                                 WBFLAG_PAM_USER_SESSION_KEY |
438                                 WBFLAG_PAM_LMKEY;
439
440                 if (params->password.response.lm_length &&
441                     !params->password.response.lm_data) {
442                         wbc_status = WBC_ERR_INVALID_PARAM;
443                         BAIL_ON_WBC_ERROR(wbc_status);
444                 }
445                 if (params->password.response.lm_length == 0 &&
446                     params->password.response.lm_data) {
447                         wbc_status = WBC_ERR_INVALID_PARAM;
448                         BAIL_ON_WBC_ERROR(wbc_status);
449                 }
450
451                 if (params->password.response.nt_length &&
452                     !params->password.response.nt_data) {
453                         wbc_status = WBC_ERR_INVALID_PARAM;
454                         BAIL_ON_WBC_ERROR(wbc_status);
455                 }
456                 if (params->password.response.nt_length == 0&&
457                     params->password.response.nt_data) {
458                         wbc_status = WBC_ERR_INVALID_PARAM;
459                         BAIL_ON_WBC_ERROR(wbc_status);
460                 }
461
462                 strncpy(request.data.auth_crap.user,
463                         params->account_name,
464                         sizeof(request.data.auth_crap.user)-1);
465                 if (params->domain_name) {
466                         strncpy(request.data.auth_crap.domain,
467                                 params->domain_name,
468                                 sizeof(request.data.auth_crap.domain)-1);
469                 }
470                 if (params->workstation_name) {
471                         strncpy(request.data.auth_crap.workstation,
472                                 params->workstation_name,
473                                 sizeof(request.data.auth_crap.workstation)-1);
474                 }
475
476                 request.data.auth_crap.logon_parameters =
477                                 params->parameter_control;
478
479                 memcpy(request.data.auth_crap.chal,
480                        params->password.response.challenge,
481                        sizeof(request.data.auth_crap.chal));
482
483                 request.data.auth_crap.lm_resp_len =
484                                 MIN(params->password.response.lm_length,
485                                     sizeof(request.data.auth_crap.lm_resp));
486                 if (params->password.response.lm_data) {
487                         memcpy(request.data.auth_crap.lm_resp,
488                                params->password.response.lm_data,
489                                request.data.auth_crap.lm_resp_len);
490                 }
491                 request.data.auth_crap.nt_resp_len = params->password.response.nt_length;
492                 if (params->password.response.nt_length > sizeof(request.data.auth_crap.nt_resp)) {
493                         request.flags |= WBFLAG_BIG_NTLMV2_BLOB;
494                         request.extra_len = params->password.response.nt_length;
495                         request.extra_data.data = (char *)malloc(
496                                 request.extra_len);
497                         if (request.extra_data.data == NULL) {
498                                 wbc_status = WBC_ERR_NO_MEMORY;
499                                 BAIL_ON_WBC_ERROR(wbc_status);
500                         }
501                         memcpy(request.extra_data.data,
502                                params->password.response.nt_data,
503                                request.data.auth_crap.nt_resp_len);
504                 } else if (params->password.response.nt_data) {
505                         memcpy(request.data.auth_crap.nt_resp,
506                                params->password.response.nt_data,
507                                request.data.auth_crap.nt_resp_len);
508                 }
509                 break;
510
511         case WBC_AUTH_USER_LEVEL_PAC:
512                 cmd = WINBINDD_PAM_AUTH_CRAP;
513                 request.flags = WBFLAG_PAM_AUTH_PAC | WBFLAG_PAM_INFO3_TEXT;
514                 request.extra_data.data = malloc(params->password.pac.length);
515                 if (request.extra_data.data == NULL) {
516                         wbc_status = WBC_ERR_NO_MEMORY;
517                         BAIL_ON_WBC_ERROR(wbc_status);
518                 }
519                 memcpy(request.extra_data.data, params->password.pac.data,
520                        params->password.pac.length);
521                 request.extra_len = params->password.pac.length;
522                 break;
523
524         default:
525                 break;
526         }
527
528         if (cmd == 0) {
529                 wbc_status = WBC_ERR_INVALID_PARAM;
530                 BAIL_ON_WBC_ERROR(wbc_status);
531         }
532
533         if (params->flags) {
534                 request.flags |= params->flags;
535         }
536
537         if (cmd == WINBINDD_PAM_AUTH_CRAP) {
538                 wbc_status = wbcRequestResponsePriv(ctx, cmd,
539                                                     &request, &response);
540         } else {
541                 wbc_status = wbcRequestResponse(ctx, cmd,
542                                                 &request, &response);
543         }
544         if (response.data.auth.nt_status != 0) {
545                 if (error) {
546                         wbc_status = wbc_create_error_info(&response,
547                                                            error);
548                         BAIL_ON_WBC_ERROR(wbc_status);
549                 }
550
551                 wbc_status = WBC_ERR_AUTH_ERROR;
552                 BAIL_ON_WBC_ERROR(wbc_status);
553         }
554         BAIL_ON_WBC_ERROR(wbc_status);
555
556         if (info) {
557                 wbc_status = wbc_create_auth_info(&response, info);
558                 BAIL_ON_WBC_ERROR(wbc_status);
559         }
560
561 done:
562         winbindd_free_response(&response);
563
564         free(request.extra_data.data);
565
566         return wbc_status;
567 }
568
569 wbcErr wbcAuthenticateUserEx(const struct wbcAuthUserParams *params,
570                              struct wbcAuthUserInfo **info,
571                              struct wbcAuthErrorInfo **error)
572 {
573         return wbcCtxAuthenticateUserEx(NULL, params, info, error);
574 }
575
576 /* Trigger a verification of the trust credentials of a specific domain */
577 wbcErr wbcCtxCheckTrustCredentials(struct wbcContext *ctx, const char *domain,
578                                    struct wbcAuthErrorInfo **error)
579 {
580         struct winbindd_request request;
581         struct winbindd_response response;
582         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
583
584         ZERO_STRUCT(request);
585         ZERO_STRUCT(response);
586
587         if (domain) {
588                 strncpy(request.domain_name, domain,
589                         sizeof(request.domain_name)-1);
590         }
591
592         /* Send request */
593
594         wbc_status = wbcRequestResponsePriv(ctx, WINBINDD_CHECK_MACHACC,
595                                             &request, &response);
596         if (response.data.auth.nt_status != 0) {
597                 if (error) {
598                         wbc_status = wbc_create_error_info(&response,
599                                                            error);
600                         BAIL_ON_WBC_ERROR(wbc_status);
601                 }
602
603                 wbc_status = WBC_ERR_AUTH_ERROR;
604                 BAIL_ON_WBC_ERROR(wbc_status);
605         }
606         BAIL_ON_WBC_ERROR(wbc_status);
607
608  done:
609         return wbc_status;
610 }
611
612 wbcErr wbcCheckTrustCredentials(const char *domain,
613                                 struct wbcAuthErrorInfo **error)
614 {
615         return wbcCtxCheckTrustCredentials(NULL, domain, error);
616 }
617
618 /* Trigger a change of the trust credentials for a specific domain */
619 wbcErr wbcCtxChangeTrustCredentials(struct wbcContext *ctx, const char *domain,
620                                     struct wbcAuthErrorInfo **error)
621 {
622         struct winbindd_request request;
623         struct winbindd_response response;
624         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
625
626         ZERO_STRUCT(request);
627         ZERO_STRUCT(response);
628
629         if (domain) {
630                 strncpy(request.domain_name, domain,
631                         sizeof(request.domain_name)-1);
632         }
633
634         /* Send request */
635
636         wbc_status = wbcRequestResponsePriv(ctx, WINBINDD_CHANGE_MACHACC,
637                                             &request, &response);
638         if (response.data.auth.nt_status != 0) {
639                 if (error) {
640                         wbc_status = wbc_create_error_info(&response,
641                                                            error);
642                         BAIL_ON_WBC_ERROR(wbc_status);
643                 }
644
645                 wbc_status = WBC_ERR_AUTH_ERROR;
646                 BAIL_ON_WBC_ERROR(wbc_status);
647         }
648         BAIL_ON_WBC_ERROR(wbc_status);
649
650  done:
651         return wbc_status;
652 }
653
654 wbcErr wbcChangeTrustCredentials(const char *domain,
655                                  struct wbcAuthErrorInfo **error)
656 {
657         return wbcCtxChangeTrustCredentials(NULL, domain, error);
658 }
659
660 /*
661  * Trigger a no-op NETLOGON call. Lightweight version of
662  * wbcCheckTrustCredentials
663  */
664 wbcErr wbcCtxPingDc(struct wbcContext *ctx, const char *domain,
665                     struct wbcAuthErrorInfo **error)
666 {
667         return wbcCtxPingDc2(ctx, domain, error, NULL);
668 }
669
670 wbcErr wbcPingDc(const char *domain, struct wbcAuthErrorInfo **error)
671 {
672         return wbcPingDc2(domain, error, NULL);
673 }
674
675 /*
676  * Trigger a no-op NETLOGON call. Lightweight version of
677  * wbcCheckTrustCredentials, optionally return attempted DC
678  */
679 wbcErr wbcCtxPingDc2(struct wbcContext *ctx, const char *domain,
680                      struct wbcAuthErrorInfo **error, char **dcname)
681 {
682         struct winbindd_request request;
683         struct winbindd_response response;
684         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
685
686         ZERO_STRUCT(request);
687         ZERO_STRUCT(response);
688
689         if (domain) {
690                 strncpy(request.domain_name, domain,
691                         sizeof(request.domain_name)-1);
692         }
693
694         /* Send request */
695
696         wbc_status = wbcRequestResponse(ctx, WINBINDD_PING_DC,
697                                         &request,
698                                         &response);
699
700         if (dcname && response.extra_data.data) {
701                 size_t len;
702
703                 len = response.length - sizeof(struct winbindd_response);
704                 *dcname = wbcAllocateMemory(1, len, NULL);
705                 BAIL_ON_PTR_ERROR(*dcname, wbc_status);
706
707                 strlcpy(*dcname, response.extra_data.data, len);
708         }
709
710         if (response.data.auth.nt_status != 0) {
711                 if (error) {
712                         wbc_status = wbc_create_error_info(&response,
713                                                            error);
714                         BAIL_ON_WBC_ERROR(wbc_status);
715                 }
716
717                 wbc_status = WBC_ERR_AUTH_ERROR;
718                 BAIL_ON_WBC_ERROR(wbc_status);
719         }
720         BAIL_ON_WBC_ERROR(wbc_status);
721
722  done:
723         return wbc_status;
724 }
725
726 wbcErr wbcPingDc2(const char *domain, struct wbcAuthErrorInfo **error,
727                   char **dcname)
728 {
729         return wbcCtxPingDc2(NULL, domain, error, dcname);
730 }
731
732 /* Trigger an extended logoff notification to Winbind for a specific user */
733 wbcErr wbcCtxLogoffUserEx(struct wbcContext *ctx,
734                           const struct wbcLogoffUserParams *params,
735                           struct wbcAuthErrorInfo **error)
736 {
737         struct winbindd_request request;
738         struct winbindd_response response;
739         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
740         size_t i;
741
742         /* validate input */
743
744         if (!params || !params->username) {
745                 wbc_status = WBC_ERR_INVALID_PARAM;
746                 BAIL_ON_WBC_ERROR(wbc_status);
747         }
748
749         if ((params->num_blobs > 0) && (params->blobs == NULL)) {
750                 wbc_status = WBC_ERR_INVALID_PARAM;
751                 BAIL_ON_WBC_ERROR(wbc_status);
752         }
753         if ((params->num_blobs == 0) && (params->blobs != NULL)) {
754                 wbc_status = WBC_ERR_INVALID_PARAM;
755                 BAIL_ON_WBC_ERROR(wbc_status);
756         }
757
758         ZERO_STRUCT(request);
759         ZERO_STRUCT(response);
760
761         strncpy(request.data.logoff.user, params->username,
762                 sizeof(request.data.logoff.user)-1);
763
764         for (i=0; i<params->num_blobs; i++) {
765
766                 if (strcasecmp(params->blobs[i].name, "ccfilename") == 0) {
767                         if (params->blobs[i].blob.data) {
768                                 strncpy(request.data.logoff.krb5ccname,
769                                         (const char *)params->blobs[i].blob.data,
770                                         sizeof(request.data.logoff.krb5ccname) - 1);
771                         }
772                         continue;
773                 }
774
775                 if (strcasecmp(params->blobs[i].name, "user_uid") == 0) {
776                         if (params->blobs[i].blob.data) {
777                                 memcpy(&request.data.logoff.uid,
778                                         params->blobs[i].blob.data,
779                                         MIN(params->blobs[i].blob.length,
780                                             sizeof(request.data.logoff.uid)));
781                         }
782                         continue;
783                 }
784
785                 if (strcasecmp(params->blobs[i].name, "flags") == 0) {
786                         if (params->blobs[i].blob.data) {
787                                 memcpy(&request.flags,
788                                         params->blobs[i].blob.data,
789                                         MIN(params->blobs[i].blob.length,
790                                             sizeof(request.flags)));
791                         }
792                         continue;
793                 }
794         }
795
796         /* Send request */
797
798         wbc_status = wbcRequestResponse(ctx, WINBINDD_PAM_LOGOFF,
799                                         &request,
800                                         &response);
801
802         /* Take the response above and return it to the caller */
803         if (response.data.auth.nt_status != 0) {
804                 if (error) {
805                         wbc_status = wbc_create_error_info(&response,
806                                                            error);
807                         BAIL_ON_WBC_ERROR(wbc_status);
808                 }
809
810                 wbc_status = WBC_ERR_AUTH_ERROR;
811                 BAIL_ON_WBC_ERROR(wbc_status);
812         }
813         BAIL_ON_WBC_ERROR(wbc_status);
814
815  done:
816         return wbc_status;
817 }
818
819 wbcErr wbcLogoffUserEx(const struct wbcLogoffUserParams *params,
820                        struct wbcAuthErrorInfo **error)
821 {
822         return wbcCtxLogoffUserEx(NULL, params, error);
823 }
824
825 /* Trigger a logoff notification to Winbind for a specific user */
826 wbcErr wbcCtxLogoffUser(struct wbcContext *ctx,
827                         const char *username, uid_t uid,
828                         const char *ccfilename)
829 {
830         struct winbindd_request request;
831         struct winbindd_response response;
832         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
833
834         /* validate input */
835
836         if (!username) {
837                 wbc_status = WBC_ERR_INVALID_PARAM;
838                 BAIL_ON_WBC_ERROR(wbc_status);
839         }
840
841         ZERO_STRUCT(request);
842         ZERO_STRUCT(response);
843
844         strncpy(request.data.logoff.user, username,
845                 sizeof(request.data.logoff.user)-1);
846         request.data.logoff.uid = uid;
847
848         if (ccfilename) {
849                 strncpy(request.data.logoff.krb5ccname, ccfilename,
850                         sizeof(request.data.logoff.krb5ccname)-1);
851         }
852
853         /* Send request */
854
855         wbc_status = wbcRequestResponse(ctx, WINBINDD_PAM_LOGOFF,
856                                         &request,
857                                         &response);
858
859         /* Take the response above and return it to the caller */
860
861  done:
862         return wbc_status;
863 }
864
865 wbcErr wbcLogoffUser(const char *username,
866                      uid_t uid,
867                      const char *ccfilename)
868 {
869         return wbcCtxLogoffUser(NULL, username, uid, ccfilename);
870 }
871
872 /* Change a password for a user with more detailed information upon failure */
873 wbcErr wbcCtxChangeUserPasswordEx(struct wbcContext *ctx,
874                         const struct wbcChangePasswordParams *params,
875                         struct wbcAuthErrorInfo **error,
876                         enum wbcPasswordChangeRejectReason *reject_reason,
877                         struct wbcUserPasswordPolicyInfo **policy)
878 {
879         struct winbindd_request request;
880         struct winbindd_response response;
881         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
882         int cmd = 0;
883
884         /* validate input */
885
886         if (!params->account_name) {
887                 wbc_status = WBC_ERR_INVALID_PARAM;
888                 goto done;
889         }
890
891         if (error) {
892                 *error = NULL;
893         }
894
895         if (policy) {
896                 *policy = NULL;
897         }
898
899         if (reject_reason) {
900                 *reject_reason = -1;
901         }
902
903         ZERO_STRUCT(request);
904         ZERO_STRUCT(response);
905
906         switch (params->level) {
907         case WBC_CHANGE_PASSWORD_LEVEL_PLAIN:
908                 cmd = WINBINDD_PAM_CHAUTHTOK;
909
910                 if (!params->account_name) {
911                         wbc_status = WBC_ERR_INVALID_PARAM;
912                         goto done;
913                 }
914
915                 strncpy(request.data.chauthtok.user, params->account_name,
916                         sizeof(request.data.chauthtok.user) - 1);
917
918                 if (params->old_password.plaintext) {
919                         strncpy(request.data.chauthtok.oldpass,
920                                 params->old_password.plaintext,
921                                 sizeof(request.data.chauthtok.oldpass) - 1);
922                 }
923
924                 if (params->new_password.plaintext) {
925                         strncpy(request.data.chauthtok.newpass,
926                                 params->new_password.plaintext,
927                                 sizeof(request.data.chauthtok.newpass) - 1);
928                 }
929                 break;
930
931         case WBC_CHANGE_PASSWORD_LEVEL_RESPONSE:
932                 cmd = WINBINDD_PAM_CHNG_PSWD_AUTH_CRAP;
933
934                 if (!params->account_name || !params->domain_name) {
935                         wbc_status = WBC_ERR_INVALID_PARAM;
936                         goto done;
937                 }
938
939                 if (params->old_password.response.old_lm_hash_enc_length &&
940                     !params->old_password.response.old_lm_hash_enc_data) {
941                         wbc_status = WBC_ERR_INVALID_PARAM;
942                         goto done;
943                 }
944
945                 if (params->old_password.response.old_lm_hash_enc_length == 0 &&
946                     params->old_password.response.old_lm_hash_enc_data) {
947                         wbc_status = WBC_ERR_INVALID_PARAM;
948                         goto done;
949                 }
950
951                 if (params->old_password.response.old_nt_hash_enc_length &&
952                     !params->old_password.response.old_nt_hash_enc_data) {
953                         wbc_status = WBC_ERR_INVALID_PARAM;
954                         goto done;
955                 }
956
957                 if (params->old_password.response.old_nt_hash_enc_length == 0 &&
958                     params->old_password.response.old_nt_hash_enc_data) {
959                         wbc_status = WBC_ERR_INVALID_PARAM;
960                         goto done;
961                 }
962
963                 if (params->new_password.response.lm_length &&
964                     !params->new_password.response.lm_data) {
965                         wbc_status = WBC_ERR_INVALID_PARAM;
966                         goto done;
967                 }
968
969                 if (params->new_password.response.lm_length == 0 &&
970                     params->new_password.response.lm_data) {
971                         wbc_status = WBC_ERR_INVALID_PARAM;
972                         goto done;
973                 }
974
975                 if (params->new_password.response.nt_length &&
976                     !params->new_password.response.nt_data) {
977                         wbc_status = WBC_ERR_INVALID_PARAM;
978                         goto done;
979                 }
980
981                 if (params->new_password.response.nt_length == 0 &&
982                     params->new_password.response.nt_data) {
983                         wbc_status = WBC_ERR_INVALID_PARAM;
984                         goto done;
985                 }
986
987                 strncpy(request.data.chng_pswd_auth_crap.user,
988                         params->account_name,
989                         sizeof(request.data.chng_pswd_auth_crap.user) - 1);
990
991                 strncpy(request.data.chng_pswd_auth_crap.domain,
992                         params->domain_name,
993                         sizeof(request.data.chng_pswd_auth_crap.domain) - 1);
994
995                 if (params->new_password.response.nt_data) {
996                         request.data.chng_pswd_auth_crap.new_nt_pswd_len =
997                                 params->new_password.response.nt_length;
998                         memcpy(request.data.chng_pswd_auth_crap.new_nt_pswd,
999                                params->new_password.response.nt_data,
1000                                request.data.chng_pswd_auth_crap.new_nt_pswd_len);
1001                 }
1002
1003                 if (params->new_password.response.lm_data) {
1004                         request.data.chng_pswd_auth_crap.new_lm_pswd_len =
1005                                 params->new_password.response.lm_length;
1006                         memcpy(request.data.chng_pswd_auth_crap.new_lm_pswd,
1007                                params->new_password.response.lm_data,
1008                                request.data.chng_pswd_auth_crap.new_lm_pswd_len);
1009                 }
1010
1011                 if (params->old_password.response.old_nt_hash_enc_data) {
1012                         request.data.chng_pswd_auth_crap.old_nt_hash_enc_len =
1013                                 params->old_password.response.old_nt_hash_enc_length;
1014                         memcpy(request.data.chng_pswd_auth_crap.old_nt_hash_enc,
1015                                params->old_password.response.old_nt_hash_enc_data,
1016                                request.data.chng_pswd_auth_crap.old_nt_hash_enc_len);
1017                 }
1018
1019                 if (params->old_password.response.old_lm_hash_enc_data) {
1020                         request.data.chng_pswd_auth_crap.old_lm_hash_enc_len =
1021                                 params->old_password.response.old_lm_hash_enc_length;
1022                         memcpy(request.data.chng_pswd_auth_crap.old_lm_hash_enc,
1023                                params->old_password.response.old_lm_hash_enc_data,
1024                                request.data.chng_pswd_auth_crap.old_lm_hash_enc_len);
1025                 }
1026
1027                 break;
1028         default:
1029                 wbc_status = WBC_ERR_INVALID_PARAM;
1030                 goto done;
1031                 break;
1032         }
1033
1034         /* Send request */
1035
1036         wbc_status = wbcRequestResponse(ctx, cmd,
1037                                         &request,
1038                                         &response);
1039         if (WBC_ERROR_IS_OK(wbc_status)) {
1040                 goto done;
1041         }
1042
1043         /* Take the response above and return it to the caller */
1044
1045         if (response.data.auth.nt_status != 0) {
1046                 if (error) {
1047                         wbc_status = wbc_create_error_info(&response,
1048                                                            error);
1049                         BAIL_ON_WBC_ERROR(wbc_status);
1050                 }
1051
1052         }
1053
1054         if (policy) {
1055                 wbc_status = wbc_create_password_policy_info(&response,
1056                                                              policy);
1057                 BAIL_ON_WBC_ERROR(wbc_status);
1058         }
1059
1060         if (reject_reason) {
1061                 *reject_reason = response.data.auth.reject_reason;
1062         }
1063
1064         wbc_status = WBC_ERR_PWD_CHANGE_FAILED;
1065         BAIL_ON_WBC_ERROR(wbc_status);
1066
1067  done:
1068         return wbc_status;
1069 }
1070
1071 wbcErr wbcChangeUserPasswordEx(const struct wbcChangePasswordParams *params,
1072                               struct wbcAuthErrorInfo **error,
1073                               enum wbcPasswordChangeRejectReason *reject_reason,
1074                               struct wbcUserPasswordPolicyInfo **policy)
1075 {
1076         return wbcCtxChangeUserPasswordEx(NULL, params, error,
1077                                           reject_reason, policy);
1078 }
1079
1080 /* Change a password for a user */
1081 wbcErr wbcCtxChangeUserPassword(struct wbcContext *ctx,
1082                                 const char *username,
1083                                 const char *old_password,
1084                                 const char *new_password)
1085 {
1086         wbcErr wbc_status = WBC_ERR_SUCCESS;
1087         struct wbcChangePasswordParams params;
1088
1089         ZERO_STRUCT(params);
1090
1091         params.account_name             = username;
1092         params.level                    = WBC_CHANGE_PASSWORD_LEVEL_PLAIN;
1093         params.old_password.plaintext   = old_password;
1094         params.new_password.plaintext   = new_password;
1095
1096         wbc_status = wbcCtxChangeUserPasswordEx(ctx, &params,
1097                                                 NULL,
1098                                                 NULL,
1099                                                 NULL);
1100         BAIL_ON_WBC_ERROR(wbc_status);
1101
1102 done:
1103         return wbc_status;
1104 }
1105
1106 wbcErr wbcChangeUserPassword(const char *username,
1107                              const char *old_password,
1108                              const char *new_password)
1109 {
1110         return wbcCtxChangeUserPassword(NULL, username,
1111                                         old_password, new_password);
1112 }
1113
1114 /* Logon a User */
1115 wbcErr wbcCtxLogonUser(struct wbcContext *ctx,
1116                        const struct wbcLogonUserParams *params,
1117                        struct wbcLogonUserInfo **info,
1118                        struct wbcAuthErrorInfo **error,
1119                        struct wbcUserPasswordPolicyInfo **policy)
1120 {
1121         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
1122         struct winbindd_request request;
1123         struct winbindd_response response;
1124         uint32_t i;
1125
1126         ZERO_STRUCT(request);
1127         ZERO_STRUCT(response);
1128
1129         if (info) {
1130                 *info = NULL;
1131         }
1132         if (error) {
1133                 *error = NULL;
1134         }
1135         if (policy) {
1136                 *policy = NULL;
1137         }
1138
1139         if (!params) {
1140                 wbc_status = WBC_ERR_INVALID_PARAM;
1141                 BAIL_ON_WBC_ERROR(wbc_status);
1142         }
1143
1144         if (!params->username) {
1145                 wbc_status = WBC_ERR_INVALID_PARAM;
1146                 BAIL_ON_WBC_ERROR(wbc_status);
1147         }
1148
1149         if ((params->num_blobs > 0) && (params->blobs == NULL)) {
1150                 wbc_status = WBC_ERR_INVALID_PARAM;
1151                 BAIL_ON_WBC_ERROR(wbc_status);
1152         }
1153         if ((params->num_blobs == 0) && (params->blobs != NULL)) {
1154                 wbc_status = WBC_ERR_INVALID_PARAM;
1155                 BAIL_ON_WBC_ERROR(wbc_status);
1156         }
1157
1158         /* Initialize request */
1159
1160         request.flags = WBFLAG_PAM_INFO3_TEXT |
1161                         WBFLAG_PAM_USER_SESSION_KEY |
1162                         WBFLAG_PAM_LMKEY;
1163
1164         if (!params->password) {
1165                 wbc_status = WBC_ERR_INVALID_PARAM;
1166                 BAIL_ON_WBC_ERROR(wbc_status);
1167         }
1168
1169         strncpy(request.data.auth.user,
1170                 params->username,
1171                 sizeof(request.data.auth.user)-1);
1172
1173         strncpy(request.data.auth.pass,
1174                 params->password,
1175                 sizeof(request.data.auth.pass)-1);
1176
1177         for (i=0; i<params->num_blobs; i++) {
1178
1179                 if (strcasecmp(params->blobs[i].name, "krb5_cc_type") == 0) {
1180                         if (params->blobs[i].blob.data) {
1181                                 strncpy(request.data.auth.krb5_cc_type,
1182                                         (const char *)params->blobs[i].blob.data,
1183                                         sizeof(request.data.auth.krb5_cc_type) - 1);
1184                         }
1185                         continue;
1186                 }
1187
1188                 if (strcasecmp(params->blobs[i].name, "user_uid") == 0) {
1189                         if (params->blobs[i].blob.data) {
1190                                 memcpy(&request.data.auth.uid,
1191                                         params->blobs[i].blob.data,
1192                                         MIN(sizeof(request.data.auth.uid),
1193                                             params->blobs[i].blob.length));
1194                         }
1195                         continue;
1196                 }
1197
1198                 if (strcasecmp(params->blobs[i].name, "flags") == 0) {
1199                         if (params->blobs[i].blob.data) {
1200                                 uint32_t flags;
1201                                 memcpy(&flags,
1202                                         params->blobs[i].blob.data,
1203                                         MIN(sizeof(flags),
1204                                             params->blobs[i].blob.length));
1205                                 request.flags |= flags;
1206                         }
1207                         continue;
1208                 }
1209
1210                 if (strcasecmp(params->blobs[i].name, "membership_of") == 0) {
1211                         if (params->blobs[i].blob.data &&
1212                             params->blobs[i].blob.data[0] > 0) {
1213                                 strncpy(request.data.auth.require_membership_of_sid,
1214                                         (const char *)params->blobs[i].blob.data,
1215                                         sizeof(request.data.auth.require_membership_of_sid) - 1);
1216                         }
1217                         continue;
1218                 }
1219         }
1220
1221         wbc_status = wbcRequestResponse(ctx, WINBINDD_PAM_AUTH,
1222                                         &request,
1223                                         &response);
1224
1225         if (response.data.auth.nt_status != 0) {
1226                 if (error) {
1227                         wbc_status = wbc_create_error_info(&response,
1228                                                            error);
1229                         BAIL_ON_WBC_ERROR(wbc_status);
1230                 }
1231
1232                 wbc_status = WBC_ERR_AUTH_ERROR;
1233                 BAIL_ON_WBC_ERROR(wbc_status);
1234         }
1235         BAIL_ON_WBC_ERROR(wbc_status);
1236
1237         if (info) {
1238                 wbc_status = wbc_create_logon_info(&response,
1239                                                    info);
1240                 BAIL_ON_WBC_ERROR(wbc_status);
1241         }
1242
1243         if (policy) {
1244                 wbc_status = wbc_create_password_policy_info(&response,
1245                                                              policy);
1246                 BAIL_ON_WBC_ERROR(wbc_status);
1247         }
1248
1249 done:
1250         winbindd_free_response(&response);
1251
1252         return wbc_status;
1253 }
1254
1255 wbcErr wbcLogonUser(const struct wbcLogonUserParams *params,
1256                     struct wbcLogonUserInfo **info,
1257                     struct wbcAuthErrorInfo **error,
1258                     struct wbcUserPasswordPolicyInfo **policy)
1259 {
1260         return wbcCtxLogonUser(NULL, params, info, error, policy);
1261 }
1262
1263 static void wbcCredentialCacheInfoDestructor(void *ptr)
1264 {
1265         struct wbcCredentialCacheInfo *i =
1266                 (struct wbcCredentialCacheInfo *)ptr;
1267         wbcFreeMemory(i->blobs);
1268 }
1269
1270 /* Authenticate a user with cached credentials */
1271 wbcErr wbcCtxCredentialCache(struct wbcContext *ctx,
1272                              struct wbcCredentialCacheParams *params,
1273                              struct wbcCredentialCacheInfo **info,
1274                              struct wbcAuthErrorInfo **error)
1275 {
1276         wbcErr status = WBC_ERR_UNKNOWN_FAILURE;
1277         struct wbcCredentialCacheInfo *result = NULL;
1278         struct winbindd_request request;
1279         struct winbindd_response response;
1280         struct wbcNamedBlob *initial_blob = NULL;
1281         struct wbcNamedBlob *challenge_blob = NULL;
1282         size_t i;
1283
1284         ZERO_STRUCT(request);
1285         ZERO_STRUCT(response);
1286
1287         *info = NULL;
1288
1289         if (error != NULL) {
1290                 *error = NULL;
1291         }
1292         if ((params == NULL)
1293             || (params->account_name == NULL)
1294             || (params->level != WBC_CREDENTIAL_CACHE_LEVEL_NTLMSSP)) {
1295                 status = WBC_ERR_INVALID_PARAM;
1296                 goto fail;
1297         }
1298
1299         for (i=0; i<params->num_blobs; i++) {
1300                 /*
1301                  * Older callers may used to provide the NEGOTIATE request
1302                  * as "initial_blob", but it was completely ignored by winbindd.
1303                  *
1304                  * So we keep ignoring it.
1305                  *
1306                  * A new callers that is capable to support "new_spnego",
1307                  * will provide the NEGOTIATE request as "negotiate_blob"
1308                  * instead.
1309                  */
1310                 if (strcasecmp(params->blobs[i].name, "negotiate_blob") == 0) {
1311                         if (initial_blob != NULL) {
1312                                 status = WBC_ERR_INVALID_PARAM;
1313                                 goto fail;
1314                         }
1315                         initial_blob = &params->blobs[i];
1316                         continue;
1317                 }
1318                 if (strcasecmp(params->blobs[i].name, "challenge_blob") == 0) {
1319                         if (challenge_blob != NULL) {
1320                                 status = WBC_ERR_INVALID_PARAM;
1321                                 goto fail;
1322                         }
1323                         challenge_blob = &params->blobs[i];
1324                         continue;
1325                 }
1326         }
1327
1328         if (params->domain_name != NULL) {
1329                 status = wbcRequestResponse(ctx, WINBINDD_INFO,
1330                                             NULL, &response);
1331                 if (!WBC_ERROR_IS_OK(status)) {
1332                         goto fail;
1333                 }
1334                 snprintf(request.data.ccache_ntlm_auth.user,
1335                          sizeof(request.data.ccache_ntlm_auth.user)-1,
1336                          "%s%c%s", params->domain_name,
1337                          response.data.info.winbind_separator,
1338                          params->account_name);
1339         } else {
1340                 strncpy(request.data.ccache_ntlm_auth.user,
1341                         params->account_name,
1342                         sizeof(request.data.ccache_ntlm_auth.user)-1);
1343         }
1344         request.data.ccache_ntlm_auth.uid = getuid();
1345
1346         request.data.ccache_ntlm_auth.initial_blob_len = 0;
1347         request.data.ccache_ntlm_auth.challenge_blob_len = 0;
1348         request.extra_len = 0;
1349
1350         if (initial_blob != NULL) {
1351                 request.data.ccache_ntlm_auth.initial_blob_len =
1352                         initial_blob->blob.length;
1353                 request.extra_len += initial_blob->blob.length;
1354         }
1355         if (challenge_blob != NULL) {
1356                 request.data.ccache_ntlm_auth.challenge_blob_len =
1357                         challenge_blob->blob.length;
1358                 request.extra_len += challenge_blob->blob.length;
1359         }
1360
1361         if (request.extra_len != 0) {
1362                 request.extra_data.data = (char *)malloc(request.extra_len);
1363                 if (request.extra_data.data == NULL) {
1364                         status = WBC_ERR_NO_MEMORY;
1365                         goto fail;
1366                 }
1367         }
1368         if (initial_blob != NULL) {
1369                 memcpy(request.extra_data.data,
1370                        initial_blob->blob.data, initial_blob->blob.length);
1371         }
1372         if (challenge_blob != NULL) {
1373                 memcpy(request.extra_data.data
1374                        + request.data.ccache_ntlm_auth.initial_blob_len,
1375                        challenge_blob->blob.data,
1376                        challenge_blob->blob.length);
1377         }
1378
1379         status = wbcRequestResponse(ctx, WINBINDD_CCACHE_NTLMAUTH,
1380                                     &request, &response);
1381         if (!WBC_ERROR_IS_OK(status)) {
1382                 goto fail;
1383         }
1384
1385         result = (struct wbcCredentialCacheInfo *)wbcAllocateMemory(
1386                 1, sizeof(struct wbcCredentialCacheInfo),
1387                 wbcCredentialCacheInfoDestructor);
1388         if (result == NULL) {
1389                 status = WBC_ERR_NO_MEMORY;
1390                 goto fail;
1391         }
1392         result->num_blobs = 0;
1393         result->blobs = NULL;
1394         status = wbcAddNamedBlob(&result->num_blobs, &result->blobs,
1395                                  "auth_blob", 0,
1396                                  (uint8_t *)response.extra_data.data,
1397                                  response.data.ccache_ntlm_auth.auth_blob_len);
1398         if (!WBC_ERROR_IS_OK(status)) {
1399                 goto fail;
1400         }
1401         status = wbcAddNamedBlob(
1402                 &result->num_blobs, &result->blobs, "session_key", 0,
1403                 response.data.ccache_ntlm_auth.session_key,
1404                 sizeof(response.data.ccache_ntlm_auth.session_key));
1405         if (!WBC_ERROR_IS_OK(status)) {
1406                 goto fail;
1407         }
1408         if (response.data.ccache_ntlm_auth.new_spnego) {
1409                 status = wbcAddNamedBlob(
1410                         &result->num_blobs, &result->blobs, "new_spnego", 0,
1411                         &response.data.ccache_ntlm_auth.new_spnego,
1412                         sizeof(response.data.ccache_ntlm_auth.new_spnego));
1413                 if (!WBC_ERROR_IS_OK(status)) {
1414                         goto fail;
1415                 }
1416         }
1417
1418         *info = result;
1419         result = NULL;
1420         status = WBC_ERR_SUCCESS;
1421 fail:
1422         free(request.extra_data.data);
1423         winbindd_free_response(&response);
1424         wbcFreeMemory(result);
1425         return status;
1426 }
1427
1428 wbcErr wbcCredentialCache(struct wbcCredentialCacheParams *params,
1429                           struct wbcCredentialCacheInfo **info,
1430                           struct wbcAuthErrorInfo **error)
1431 {
1432         return wbcCtxCredentialCache(NULL, params, info, error);
1433 }
1434
1435 /* Authenticate a user with cached credentials */
1436 wbcErr wbcCtxCredentialSave(struct wbcContext *ctx,
1437                             const char *user, const char *password)
1438 {
1439         struct winbindd_request request;
1440         struct winbindd_response response;
1441
1442         ZERO_STRUCT(request);
1443         ZERO_STRUCT(response);
1444
1445         strncpy(request.data.ccache_save.user, user,
1446                 sizeof(request.data.ccache_save.user)-1);
1447         strncpy(request.data.ccache_save.pass, password,
1448                 sizeof(request.data.ccache_save.pass)-1);
1449         request.data.ccache_save.uid = getuid();
1450
1451         return wbcRequestResponse(ctx, WINBINDD_CCACHE_SAVE, &request, &response);
1452 }
1453
1454 wbcErr wbcCredentialSave(const char *user, const char *password)
1455 {
1456         return wbcCtxCredentialSave(NULL, user, password);
1457 }