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