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