check return value before using key_values
[Samba.git] / nsswitch / libwbclient / wbc_pam.c
blobe4cd29630128e87a35336ceef7bb9b9bc6cd2819
1 /*
2 Unix SMB/CIFS implementation.
4 Winbind client API
6 Copyright (C) Gerald (Jerry) Carter 2007
7 Copyright (C) Guenther Deschner 2008
8 Copyright (C) Volker Lendecke 2009
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.
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.
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/>.
24 /* Required Headers */
26 #include "replace.h"
27 #include "libwbclient.h"
28 #include "../winbind_client.h"
30 /* Authenticate a username/password pair */
31 wbcErr wbcCtxAuthenticateUser(struct wbcContext *ctx,
32 const char *username, const char *password)
34 wbcErr wbc_status = WBC_ERR_SUCCESS;
35 struct wbcAuthUserParams params;
37 ZERO_STRUCT(params);
39 params.account_name = username;
40 params.level = WBC_AUTH_USER_LEVEL_PLAIN;
41 params.password.plaintext = password;
43 wbc_status = wbcCtxAuthenticateUserEx(ctx, &params, NULL, NULL);
44 BAIL_ON_WBC_ERROR(wbc_status);
46 done:
47 return wbc_status;
50 wbcErr wbcAuthenticateUser(const char *username, const char *password)
52 return wbcCtxAuthenticateUser(NULL, username, password);
55 static bool sid_attr_compose(struct wbcSidWithAttr *s,
56 const struct wbcDomainSid *d,
57 uint32_t rid, uint32_t attr)
59 if (d->num_auths >= WBC_MAXSUBAUTHS) {
60 return false;
62 s->sid = *d;
63 s->sid.sub_auths[s->sid.num_auths++] = rid;
64 s->attributes = attr;
65 return true;
68 static void wbcAuthUserInfoDestructor(void *ptr)
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);
84 static wbcErr wbc_create_auth_info(const struct winbindd_response *resp,
85 struct wbcAuthUserInfo **_i)
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;
94 i = (struct wbcAuthUserInfo *)wbcAllocateMemory(
95 1, sizeof(struct wbcAuthUserInfo),
96 wbcAuthUserInfoDestructor);
97 BAIL_ON_PTR_ERROR(i, wbc_status);
99 i->user_flags = resp->data.auth.info3.user_flgs;
101 i->account_name = strdup(resp->data.auth.info3.user_name);
102 BAIL_ON_PTR_ERROR(i->account_name, wbc_status);
103 if (resp->data.auth.validation_level == 6) {
104 i->user_principal = strdup(resp->data.auth.info6.principal_name);
105 BAIL_ON_PTR_ERROR(i->user_principal, wbc_status);
106 } else {
107 i->user_principal = NULL;
109 i->full_name = strdup(resp->data.auth.info3.full_name);
110 BAIL_ON_PTR_ERROR(i->full_name, wbc_status);
111 i->domain_name = strdup(resp->data.auth.info3.logon_dom);
112 BAIL_ON_PTR_ERROR(i->domain_name, wbc_status);
113 if (resp->data.auth.validation_level == 6) {
114 i->dns_domain_name = strdup(resp->data.auth.info6.dns_domainname);
115 BAIL_ON_PTR_ERROR(i->dns_domain_name, wbc_status);
116 } else {
117 i->dns_domain_name = NULL;
120 i->acct_flags = resp->data.auth.info3.acct_flags;
121 memcpy(i->user_session_key,
122 resp->data.auth.user_session_key,
123 sizeof(i->user_session_key));
124 memcpy(i->lm_session_key,
125 resp->data.auth.first_8_lm_hash,
126 sizeof(i->lm_session_key));
128 i->logon_count = resp->data.auth.info3.logon_count;
129 i->bad_password_count = resp->data.auth.info3.bad_pw_count;
131 i->logon_time = resp->data.auth.info3.logon_time;
132 i->logoff_time = resp->data.auth.info3.logoff_time;
133 i->kickoff_time = resp->data.auth.info3.kickoff_time;
134 i->pass_last_set_time = resp->data.auth.info3.pass_last_set_time;
135 i->pass_can_change_time = resp->data.auth.info3.pass_can_change_time;
136 i->pass_must_change_time= resp->data.auth.info3.pass_must_change_time;
138 i->logon_server = strdup(resp->data.auth.info3.logon_srv);
139 BAIL_ON_PTR_ERROR(i->logon_server, wbc_status);
140 i->logon_script = strdup(resp->data.auth.info3.logon_script);
141 BAIL_ON_PTR_ERROR(i->logon_script, wbc_status);
142 i->profile_path = strdup(resp->data.auth.info3.profile_path);
143 BAIL_ON_PTR_ERROR(i->profile_path, wbc_status);
144 i->home_directory= strdup(resp->data.auth.info3.home_dir);
145 BAIL_ON_PTR_ERROR(i->home_directory, wbc_status);
146 i->home_drive = strdup(resp->data.auth.info3.dir_drive);
147 BAIL_ON_PTR_ERROR(i->home_drive, wbc_status);
149 i->num_sids = 2;
150 i->num_sids += resp->data.auth.info3.num_groups;
151 i->num_sids += resp->data.auth.info3.num_other_sids;
153 i->sids = (struct wbcSidWithAttr *)calloc(
154 sizeof(struct wbcSidWithAttr), i->num_sids);
155 BAIL_ON_PTR_ERROR(i->sids, wbc_status);
157 wbc_status = wbcStringToSid(resp->data.auth.info3.dom_sid,
158 &domain_sid);
159 BAIL_ON_WBC_ERROR(wbc_status);
161 sn = 0;
162 if (!sid_attr_compose(&i->sids[sn], &domain_sid,
163 resp->data.auth.info3.user_rid, 0)) {
164 wbc_status = WBC_ERR_INVALID_SID;
165 goto done;
167 sn++;
168 if (!sid_attr_compose(&i->sids[sn], &domain_sid,
169 resp->data.auth.info3.group_rid, 0)) {
170 wbc_status = WBC_ERR_INVALID_SID;
171 goto done;
173 sn++;
175 p = (char *)resp->extra_data.data;
176 if (!p) {
177 wbc_status = WBC_ERR_INVALID_RESPONSE;
178 BAIL_ON_WBC_ERROR(wbc_status);
181 for (j=0; j < resp->data.auth.info3.num_groups; j++) {
182 uint32_t rid;
183 uint32_t attrs;
184 int ret;
185 char *s = p;
186 char *e = strchr(p, '\n');
187 if (!e) {
188 wbc_status = WBC_ERR_INVALID_RESPONSE;
189 BAIL_ON_WBC_ERROR(wbc_status);
191 e[0] = '\0';
192 p = &e[1];
194 ret = sscanf(s, "0x%08X:0x%08X", &rid, &attrs);
195 if (ret != 2) {
196 wbc_status = WBC_ERR_INVALID_RESPONSE;
197 BAIL_ON_WBC_ERROR(wbc_status);
200 if (!sid_attr_compose(&i->sids[sn], &domain_sid,
201 rid, attrs)) {
202 wbc_status = WBC_ERR_INVALID_SID;
203 goto done;
205 sn++;
208 for (j=0; j < resp->data.auth.info3.num_other_sids; j++) {
209 uint32_t attrs;
210 int ret;
211 char *s = p;
212 char *a;
213 char *e = strchr(p, '\n');
214 if (!e) {
215 wbc_status = WBC_ERR_INVALID_RESPONSE;
216 BAIL_ON_WBC_ERROR(wbc_status);
218 e[0] = '\0';
219 p = &e[1];
221 e = strchr(s, ':');
222 if (!e) {
223 wbc_status = WBC_ERR_INVALID_RESPONSE;
224 BAIL_ON_WBC_ERROR(wbc_status);
226 e[0] = '\0';
227 a = &e[1];
229 ret = sscanf(a, "0x%08X",
230 &attrs);
231 if (ret != 1) {
232 wbc_status = WBC_ERR_INVALID_RESPONSE;
233 BAIL_ON_WBC_ERROR(wbc_status);
236 wbc_status = wbcStringToSid(s, &i->sids[sn].sid);
237 BAIL_ON_WBC_ERROR(wbc_status);
239 i->sids[sn].attributes = attrs;
240 sn++;
243 i->num_sids = sn;
245 *_i = i;
246 i = NULL;
247 done:
248 wbcFreeMemory(i);
249 return wbc_status;
252 static void wbcAuthErrorInfoDestructor(void *ptr)
254 struct wbcAuthErrorInfo *e = (struct wbcAuthErrorInfo *)ptr;
255 free(e->nt_string);
256 free(e->display_string);
259 static wbcErr wbc_create_error_info(const struct winbindd_response *resp,
260 struct wbcAuthErrorInfo **_e)
262 wbcErr wbc_status = WBC_ERR_SUCCESS;
263 struct wbcAuthErrorInfo *e;
265 e = (struct wbcAuthErrorInfo *)wbcAllocateMemory(
266 1, sizeof(struct wbcAuthErrorInfo),
267 wbcAuthErrorInfoDestructor);
268 BAIL_ON_PTR_ERROR(e, wbc_status);
270 e->nt_status = resp->data.auth.nt_status;
271 e->pam_error = resp->data.auth.pam_error;
272 e->authoritative = resp->data.auth.authoritative;
273 e->nt_string = strdup(resp->data.auth.nt_status_string);
274 BAIL_ON_PTR_ERROR(e->nt_string, wbc_status);
276 e->display_string = strdup(resp->data.auth.error_string);
277 BAIL_ON_PTR_ERROR(e->display_string, wbc_status);
279 *_e = e;
280 e = NULL;
282 done:
283 wbcFreeMemory(e);
284 return wbc_status;
287 static wbcErr wbc_create_password_policy_info(const struct winbindd_response *resp,
288 struct wbcUserPasswordPolicyInfo **_i)
290 wbcErr wbc_status = WBC_ERR_SUCCESS;
291 struct wbcUserPasswordPolicyInfo *i;
293 i = (struct wbcUserPasswordPolicyInfo *)wbcAllocateMemory(
294 1, sizeof(struct wbcUserPasswordPolicyInfo), NULL);
295 BAIL_ON_PTR_ERROR(i, wbc_status);
297 i->min_passwordage = resp->data.auth.policy.min_passwordage;
298 i->min_length_password = resp->data.auth.policy.min_length_password;
299 i->password_history = resp->data.auth.policy.password_history;
300 i->password_properties = resp->data.auth.policy.password_properties;
301 i->expire = resp->data.auth.policy.expire;
303 *_i = i;
304 i = NULL;
306 done:
307 wbcFreeMemory(i);
308 return wbc_status;
311 static void wbcLogonUserInfoDestructor(void *ptr)
313 struct wbcLogonUserInfo *i = (struct wbcLogonUserInfo *)ptr;
314 wbcFreeMemory(i->info);
315 wbcFreeMemory(i->blobs);
318 static wbcErr wbc_create_logon_info(struct winbindd_response *resp,
319 struct wbcLogonUserInfo **_i)
321 wbcErr wbc_status = WBC_ERR_SUCCESS;
322 struct wbcLogonUserInfo *i;
324 i = (struct wbcLogonUserInfo *)wbcAllocateMemory(
325 1, sizeof(struct wbcLogonUserInfo),
326 wbcLogonUserInfoDestructor);
327 BAIL_ON_PTR_ERROR(i, wbc_status);
329 wbc_status = wbc_create_auth_info(resp, &i->info);
330 BAIL_ON_WBC_ERROR(wbc_status);
332 if (resp->data.auth.krb5ccname[0] != '\0') {
333 wbc_status = wbcAddNamedBlob(&i->num_blobs,
334 &i->blobs,
335 "krb5ccname",
337 (uint8_t *)resp->data.auth.krb5ccname,
338 strlen(resp->data.auth.krb5ccname)+1);
339 BAIL_ON_WBC_ERROR(wbc_status);
342 if (resp->data.auth.unix_username[0] != '\0') {
343 wbc_status = wbcAddNamedBlob(&i->num_blobs,
344 &i->blobs,
345 "unix_username",
347 (uint8_t *)resp->data.auth.unix_username,
348 strlen(resp->data.auth.unix_username)+1);
349 BAIL_ON_WBC_ERROR(wbc_status);
352 *_i = i;
353 i = NULL;
354 done:
355 wbcFreeMemory(i);
356 return wbc_status;
360 /* Authenticate with more detailed information */
361 wbcErr wbcCtxAuthenticateUserEx(struct wbcContext *ctx,
362 const struct wbcAuthUserParams *params,
363 struct wbcAuthUserInfo **info,
364 struct wbcAuthErrorInfo **error)
366 wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
367 int cmd = 0;
368 struct winbindd_request request;
369 struct winbindd_response response;
371 ZERO_STRUCT(request);
372 ZERO_STRUCT(response);
374 if (error) {
375 *error = NULL;
378 if (!params) {
379 wbc_status = WBC_ERR_INVALID_PARAM;
380 BAIL_ON_WBC_ERROR(wbc_status);
383 if (params->level != WBC_AUTH_USER_LEVEL_PAC && !params->account_name) {
384 wbc_status = WBC_ERR_INVALID_PARAM;
385 BAIL_ON_WBC_ERROR(wbc_status);
388 /* Initialize request */
390 switch (params->level) {
391 case WBC_AUTH_USER_LEVEL_PLAIN:
392 cmd = WINBINDD_PAM_AUTH;
393 request.flags = WBFLAG_PAM_INFO3_TEXT |
394 WBFLAG_PAM_USER_SESSION_KEY |
395 WBFLAG_PAM_LMKEY;
397 if (!params->password.plaintext) {
398 wbc_status = WBC_ERR_INVALID_PARAM;
399 BAIL_ON_WBC_ERROR(wbc_status);
402 if (params->domain_name && params->domain_name[0]) {
403 /* We need to get the winbind separator :-( */
404 struct winbindd_response sep_response;
406 ZERO_STRUCT(sep_response);
408 wbc_status = wbcRequestResponse(ctx, WINBINDD_INFO,
409 NULL, &sep_response);
410 BAIL_ON_WBC_ERROR(wbc_status);
412 snprintf(request.data.auth.user,
413 sizeof(request.data.auth.user)-1,
414 "%s%c%s",
415 params->domain_name,
416 sep_response.data.info.winbind_separator,
417 params->account_name);
418 } else {
419 strncpy(request.data.auth.user,
420 params->account_name,
421 sizeof(request.data.auth.user)-1);
424 strncpy(request.data.auth.pass,
425 params->password.plaintext,
426 sizeof(request.data.auth.pass)-1);
427 break;
429 case WBC_AUTH_USER_LEVEL_HASH:
430 wbc_status = WBC_ERR_NOT_IMPLEMENTED;
431 BAIL_ON_WBC_ERROR(wbc_status);
432 break;
434 case WBC_AUTH_USER_LEVEL_RESPONSE:
435 cmd = WINBINDD_PAM_AUTH_CRAP;
436 request.flags = WBFLAG_PAM_INFO3_TEXT |
437 WBFLAG_PAM_USER_SESSION_KEY |
438 WBFLAG_PAM_LMKEY;
440 if (params->password.response.lm_length &&
441 !params->password.response.lm_data) {
442 wbc_status = WBC_ERR_INVALID_PARAM;
443 BAIL_ON_WBC_ERROR(wbc_status);
445 if (params->password.response.lm_length == 0 &&
446 params->password.response.lm_data) {
447 wbc_status = WBC_ERR_INVALID_PARAM;
448 BAIL_ON_WBC_ERROR(wbc_status);
451 if (params->password.response.nt_length &&
452 !params->password.response.nt_data) {
453 wbc_status = WBC_ERR_INVALID_PARAM;
454 BAIL_ON_WBC_ERROR(wbc_status);
456 if (params->password.response.nt_length == 0&&
457 params->password.response.nt_data) {
458 wbc_status = WBC_ERR_INVALID_PARAM;
459 BAIL_ON_WBC_ERROR(wbc_status);
462 strncpy(request.data.auth_crap.user,
463 params->account_name,
464 sizeof(request.data.auth_crap.user)-1);
465 if (params->domain_name) {
466 strncpy(request.data.auth_crap.domain,
467 params->domain_name,
468 sizeof(request.data.auth_crap.domain)-1);
470 if (params->workstation_name) {
471 strncpy(request.data.auth_crap.workstation,
472 params->workstation_name,
473 sizeof(request.data.auth_crap.workstation)-1);
476 request.data.auth_crap.logon_parameters =
477 params->parameter_control;
479 memcpy(request.data.auth_crap.chal,
480 params->password.response.challenge,
481 sizeof(request.data.auth_crap.chal));
483 request.data.auth_crap.lm_resp_len =
484 MIN(params->password.response.lm_length,
485 sizeof(request.data.auth_crap.lm_resp));
486 if (params->password.response.lm_data) {
487 memcpy(request.data.auth_crap.lm_resp,
488 params->password.response.lm_data,
489 request.data.auth_crap.lm_resp_len);
491 request.data.auth_crap.nt_resp_len = params->password.response.nt_length;
492 if (params->password.response.nt_length > sizeof(request.data.auth_crap.nt_resp)) {
493 request.flags |= WBFLAG_BIG_NTLMV2_BLOB;
494 request.extra_len = params->password.response.nt_length;
495 request.extra_data.data = (char *)malloc(
496 request.extra_len);
497 if (request.extra_data.data == NULL) {
498 wbc_status = WBC_ERR_NO_MEMORY;
499 BAIL_ON_WBC_ERROR(wbc_status);
501 memcpy(request.extra_data.data,
502 params->password.response.nt_data,
503 request.data.auth_crap.nt_resp_len);
504 } else if (params->password.response.nt_data) {
505 memcpy(request.data.auth_crap.nt_resp,
506 params->password.response.nt_data,
507 request.data.auth_crap.nt_resp_len);
509 break;
511 case WBC_AUTH_USER_LEVEL_PAC:
512 cmd = WINBINDD_PAM_AUTH_CRAP;
513 request.flags = WBFLAG_PAM_AUTH_PAC | WBFLAG_PAM_INFO3_TEXT;
514 request.extra_data.data = malloc(params->password.pac.length);
515 if (request.extra_data.data == NULL) {
516 wbc_status = WBC_ERR_NO_MEMORY;
517 BAIL_ON_WBC_ERROR(wbc_status);
519 memcpy(request.extra_data.data, params->password.pac.data,
520 params->password.pac.length);
521 request.extra_len = params->password.pac.length;
522 break;
524 default:
525 break;
528 if (cmd == 0) {
529 wbc_status = WBC_ERR_INVALID_PARAM;
530 BAIL_ON_WBC_ERROR(wbc_status);
533 if (params->flags) {
534 request.flags |= params->flags;
537 if (cmd == WINBINDD_PAM_AUTH_CRAP) {
538 wbc_status = wbcRequestResponsePriv(ctx, cmd,
539 &request, &response);
540 } else {
541 wbc_status = wbcRequestResponse(ctx, cmd,
542 &request, &response);
544 if (response.data.auth.nt_status != 0) {
545 if (error) {
546 wbc_status = wbc_create_error_info(&response,
547 error);
548 BAIL_ON_WBC_ERROR(wbc_status);
551 wbc_status = WBC_ERR_AUTH_ERROR;
552 BAIL_ON_WBC_ERROR(wbc_status);
554 BAIL_ON_WBC_ERROR(wbc_status);
556 if (info) {
557 wbc_status = wbc_create_auth_info(&response, info);
558 BAIL_ON_WBC_ERROR(wbc_status);
561 done:
562 winbindd_free_response(&response);
564 free(request.extra_data.data);
566 return wbc_status;
569 wbcErr wbcAuthenticateUserEx(const struct wbcAuthUserParams *params,
570 struct wbcAuthUserInfo **info,
571 struct wbcAuthErrorInfo **error)
573 return wbcCtxAuthenticateUserEx(NULL, params, info, error);
576 /* Trigger a verification of the trust credentials of a specific domain */
577 wbcErr wbcCtxCheckTrustCredentials(struct wbcContext *ctx, const char *domain,
578 struct wbcAuthErrorInfo **error)
580 struct winbindd_request request;
581 struct winbindd_response response;
582 wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
584 ZERO_STRUCT(request);
585 ZERO_STRUCT(response);
587 if (domain) {
588 strncpy(request.domain_name, domain,
589 sizeof(request.domain_name)-1);
592 /* Send request */
594 wbc_status = wbcRequestResponsePriv(ctx, WINBINDD_CHECK_MACHACC,
595 &request, &response);
596 if (response.data.auth.nt_status != 0) {
597 if (error) {
598 wbc_status = wbc_create_error_info(&response,
599 error);
600 BAIL_ON_WBC_ERROR(wbc_status);
603 wbc_status = WBC_ERR_AUTH_ERROR;
604 BAIL_ON_WBC_ERROR(wbc_status);
606 BAIL_ON_WBC_ERROR(wbc_status);
608 done:
609 return wbc_status;
612 wbcErr wbcCheckTrustCredentials(const char *domain,
613 struct wbcAuthErrorInfo **error)
615 return wbcCtxCheckTrustCredentials(NULL, domain, error);
618 /* Trigger a change of the trust credentials for a specific domain */
619 wbcErr wbcCtxChangeTrustCredentials(struct wbcContext *ctx, const char *domain,
620 struct wbcAuthErrorInfo **error)
622 struct winbindd_request request;
623 struct winbindd_response response;
624 wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
626 ZERO_STRUCT(request);
627 ZERO_STRUCT(response);
629 if (domain) {
630 strncpy(request.domain_name, domain,
631 sizeof(request.domain_name)-1);
634 /* Send request */
636 wbc_status = wbcRequestResponsePriv(ctx, WINBINDD_CHANGE_MACHACC,
637 &request, &response);
638 if (response.data.auth.nt_status != 0) {
639 if (error) {
640 wbc_status = wbc_create_error_info(&response,
641 error);
642 BAIL_ON_WBC_ERROR(wbc_status);
645 wbc_status = WBC_ERR_AUTH_ERROR;
646 BAIL_ON_WBC_ERROR(wbc_status);
648 BAIL_ON_WBC_ERROR(wbc_status);
650 done:
651 return wbc_status;
654 wbcErr wbcChangeTrustCredentials(const char *domain,
655 struct wbcAuthErrorInfo **error)
657 return wbcCtxChangeTrustCredentials(NULL, domain, error);
661 * Trigger a no-op NETLOGON call. Lightweight version of
662 * wbcCheckTrustCredentials
664 wbcErr wbcCtxPingDc(struct wbcContext *ctx, const char *domain,
665 struct wbcAuthErrorInfo **error)
667 return wbcCtxPingDc2(ctx, domain, error, NULL);
670 wbcErr wbcPingDc(const char *domain, struct wbcAuthErrorInfo **error)
672 return wbcPingDc2(domain, error, NULL);
676 * Trigger a no-op NETLOGON call. Lightweight version of
677 * wbcCheckTrustCredentials, optionally return attempted DC
679 wbcErr wbcCtxPingDc2(struct wbcContext *ctx, const char *domain,
680 struct wbcAuthErrorInfo **error, char **dcname)
682 struct winbindd_request request;
683 struct winbindd_response response;
684 wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
686 ZERO_STRUCT(request);
687 ZERO_STRUCT(response);
689 if (domain) {
690 strncpy(request.domain_name, domain,
691 sizeof(request.domain_name)-1);
694 /* Send request */
696 wbc_status = wbcRequestResponse(ctx, WINBINDD_PING_DC,
697 &request,
698 &response);
700 if (dcname && response.extra_data.data) {
701 size_t len;
703 len = response.length - sizeof(struct winbindd_response);
704 *dcname = wbcAllocateMemory(1, len, NULL);
705 BAIL_ON_PTR_ERROR(*dcname, wbc_status);
707 strlcpy(*dcname, response.extra_data.data, len);
710 if (response.data.auth.nt_status != 0) {
711 if (error) {
712 wbc_status = wbc_create_error_info(&response,
713 error);
714 BAIL_ON_WBC_ERROR(wbc_status);
717 wbc_status = WBC_ERR_AUTH_ERROR;
718 BAIL_ON_WBC_ERROR(wbc_status);
720 BAIL_ON_WBC_ERROR(wbc_status);
722 done:
723 return wbc_status;
726 wbcErr wbcPingDc2(const char *domain, struct wbcAuthErrorInfo **error,
727 char **dcname)
729 return wbcCtxPingDc2(NULL, domain, error, dcname);
732 /* Trigger an extended logoff notification to Winbind for a specific user */
733 wbcErr wbcCtxLogoffUserEx(struct wbcContext *ctx,
734 const struct wbcLogoffUserParams *params,
735 struct wbcAuthErrorInfo **error)
737 struct winbindd_request request;
738 struct winbindd_response response;
739 wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
740 size_t i;
742 /* validate input */
744 if (!params || !params->username) {
745 wbc_status = WBC_ERR_INVALID_PARAM;
746 BAIL_ON_WBC_ERROR(wbc_status);
749 if ((params->num_blobs > 0) && (params->blobs == NULL)) {
750 wbc_status = WBC_ERR_INVALID_PARAM;
751 BAIL_ON_WBC_ERROR(wbc_status);
753 if ((params->num_blobs == 0) && (params->blobs != NULL)) {
754 wbc_status = WBC_ERR_INVALID_PARAM;
755 BAIL_ON_WBC_ERROR(wbc_status);
758 ZERO_STRUCT(request);
759 ZERO_STRUCT(response);
761 strncpy(request.data.logoff.user, params->username,
762 sizeof(request.data.logoff.user)-1);
764 for (i=0; i<params->num_blobs; i++) {
766 if (strcasecmp(params->blobs[i].name, "ccfilename") == 0) {
767 if (params->blobs[i].blob.data) {
768 strncpy(request.data.logoff.krb5ccname,
769 (const char *)params->blobs[i].blob.data,
770 sizeof(request.data.logoff.krb5ccname) - 1);
772 continue;
775 if (strcasecmp(params->blobs[i].name, "user_uid") == 0) {
776 if (params->blobs[i].blob.data) {
777 memcpy(&request.data.logoff.uid,
778 params->blobs[i].blob.data,
779 MIN(params->blobs[i].blob.length,
780 sizeof(request.data.logoff.uid)));
782 continue;
785 if (strcasecmp(params->blobs[i].name, "flags") == 0) {
786 if (params->blobs[i].blob.data) {
787 memcpy(&request.flags,
788 params->blobs[i].blob.data,
789 MIN(params->blobs[i].blob.length,
790 sizeof(request.flags)));
792 continue;
796 /* Send request */
798 wbc_status = wbcRequestResponse(ctx, WINBINDD_PAM_LOGOFF,
799 &request,
800 &response);
802 /* Take the response above and return it to the caller */
803 if (response.data.auth.nt_status != 0) {
804 if (error) {
805 wbc_status = wbc_create_error_info(&response,
806 error);
807 BAIL_ON_WBC_ERROR(wbc_status);
810 wbc_status = WBC_ERR_AUTH_ERROR;
811 BAIL_ON_WBC_ERROR(wbc_status);
813 BAIL_ON_WBC_ERROR(wbc_status);
815 done:
816 return wbc_status;
819 wbcErr wbcLogoffUserEx(const struct wbcLogoffUserParams *params,
820 struct wbcAuthErrorInfo **error)
822 return wbcCtxLogoffUserEx(NULL, params, error);
825 /* Trigger a logoff notification to Winbind for a specific user */
826 wbcErr wbcCtxLogoffUser(struct wbcContext *ctx,
827 const char *username, uid_t uid,
828 const char *ccfilename)
830 struct winbindd_request request;
831 struct winbindd_response response;
832 wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
834 /* validate input */
836 if (!username) {
837 wbc_status = WBC_ERR_INVALID_PARAM;
838 BAIL_ON_WBC_ERROR(wbc_status);
841 ZERO_STRUCT(request);
842 ZERO_STRUCT(response);
844 strncpy(request.data.logoff.user, username,
845 sizeof(request.data.logoff.user)-1);
846 request.data.logoff.uid = uid;
848 if (ccfilename) {
849 strncpy(request.data.logoff.krb5ccname, ccfilename,
850 sizeof(request.data.logoff.krb5ccname)-1);
853 /* Send request */
855 wbc_status = wbcRequestResponse(ctx, WINBINDD_PAM_LOGOFF,
856 &request,
857 &response);
859 /* Take the response above and return it to the caller */
861 done:
862 return wbc_status;
865 wbcErr wbcLogoffUser(const char *username,
866 uid_t uid,
867 const char *ccfilename)
869 return wbcCtxLogoffUser(NULL, username, uid, ccfilename);
872 /* Change a password for a user with more detailed information upon failure */
873 wbcErr wbcCtxChangeUserPasswordEx(struct wbcContext *ctx,
874 const struct wbcChangePasswordParams *params,
875 struct wbcAuthErrorInfo **error,
876 enum wbcPasswordChangeRejectReason *reject_reason,
877 struct wbcUserPasswordPolicyInfo **policy)
879 struct winbindd_request request;
880 struct winbindd_response response;
881 wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
882 int cmd = 0;
884 /* validate input */
886 if (!params->account_name) {
887 wbc_status = WBC_ERR_INVALID_PARAM;
888 goto done;
891 if (error) {
892 *error = NULL;
895 if (policy) {
896 *policy = NULL;
899 if (reject_reason) {
900 *reject_reason = -1;
903 ZERO_STRUCT(request);
904 ZERO_STRUCT(response);
906 switch (params->level) {
907 case WBC_CHANGE_PASSWORD_LEVEL_PLAIN:
908 cmd = WINBINDD_PAM_CHAUTHTOK;
910 if (!params->account_name) {
911 wbc_status = WBC_ERR_INVALID_PARAM;
912 goto done;
915 strncpy(request.data.chauthtok.user, params->account_name,
916 sizeof(request.data.chauthtok.user) - 1);
918 if (params->old_password.plaintext) {
919 strncpy(request.data.chauthtok.oldpass,
920 params->old_password.plaintext,
921 sizeof(request.data.chauthtok.oldpass) - 1);
924 if (params->new_password.plaintext) {
925 strncpy(request.data.chauthtok.newpass,
926 params->new_password.plaintext,
927 sizeof(request.data.chauthtok.newpass) - 1);
929 break;
931 case WBC_CHANGE_PASSWORD_LEVEL_RESPONSE:
932 cmd = WINBINDD_PAM_CHNG_PSWD_AUTH_CRAP;
934 if (!params->account_name || !params->domain_name) {
935 wbc_status = WBC_ERR_INVALID_PARAM;
936 goto done;
939 if (params->old_password.response.old_lm_hash_enc_length &&
940 !params->old_password.response.old_lm_hash_enc_data) {
941 wbc_status = WBC_ERR_INVALID_PARAM;
942 goto done;
945 if (params->old_password.response.old_lm_hash_enc_length == 0 &&
946 params->old_password.response.old_lm_hash_enc_data) {
947 wbc_status = WBC_ERR_INVALID_PARAM;
948 goto done;
951 if (params->old_password.response.old_nt_hash_enc_length &&
952 !params->old_password.response.old_nt_hash_enc_data) {
953 wbc_status = WBC_ERR_INVALID_PARAM;
954 goto done;
957 if (params->old_password.response.old_nt_hash_enc_length == 0 &&
958 params->old_password.response.old_nt_hash_enc_data) {
959 wbc_status = WBC_ERR_INVALID_PARAM;
960 goto done;
963 if (params->new_password.response.lm_length &&
964 !params->new_password.response.lm_data) {
965 wbc_status = WBC_ERR_INVALID_PARAM;
966 goto done;
969 if (params->new_password.response.lm_length == 0 &&
970 params->new_password.response.lm_data) {
971 wbc_status = WBC_ERR_INVALID_PARAM;
972 goto done;
975 if (params->new_password.response.nt_length &&
976 !params->new_password.response.nt_data) {
977 wbc_status = WBC_ERR_INVALID_PARAM;
978 goto done;
981 if (params->new_password.response.nt_length == 0 &&
982 params->new_password.response.nt_data) {
983 wbc_status = WBC_ERR_INVALID_PARAM;
984 goto done;
987 strncpy(request.data.chng_pswd_auth_crap.user,
988 params->account_name,
989 sizeof(request.data.chng_pswd_auth_crap.user) - 1);
991 strncpy(request.data.chng_pswd_auth_crap.domain,
992 params->domain_name,
993 sizeof(request.data.chng_pswd_auth_crap.domain) - 1);
995 if (params->new_password.response.nt_data) {
996 request.data.chng_pswd_auth_crap.new_nt_pswd_len =
997 params->new_password.response.nt_length;
998 memcpy(request.data.chng_pswd_auth_crap.new_nt_pswd,
999 params->new_password.response.nt_data,
1000 request.data.chng_pswd_auth_crap.new_nt_pswd_len);
1003 if (params->new_password.response.lm_data) {
1004 request.data.chng_pswd_auth_crap.new_lm_pswd_len =
1005 params->new_password.response.lm_length;
1006 memcpy(request.data.chng_pswd_auth_crap.new_lm_pswd,
1007 params->new_password.response.lm_data,
1008 request.data.chng_pswd_auth_crap.new_lm_pswd_len);
1011 if (params->old_password.response.old_nt_hash_enc_data) {
1012 request.data.chng_pswd_auth_crap.old_nt_hash_enc_len =
1013 params->old_password.response.old_nt_hash_enc_length;
1014 memcpy(request.data.chng_pswd_auth_crap.old_nt_hash_enc,
1015 params->old_password.response.old_nt_hash_enc_data,
1016 request.data.chng_pswd_auth_crap.old_nt_hash_enc_len);
1019 if (params->old_password.response.old_lm_hash_enc_data) {
1020 request.data.chng_pswd_auth_crap.old_lm_hash_enc_len =
1021 params->old_password.response.old_lm_hash_enc_length;
1022 memcpy(request.data.chng_pswd_auth_crap.old_lm_hash_enc,
1023 params->old_password.response.old_lm_hash_enc_data,
1024 request.data.chng_pswd_auth_crap.old_lm_hash_enc_len);
1027 break;
1028 default:
1029 wbc_status = WBC_ERR_INVALID_PARAM;
1030 goto done;
1031 break;
1034 /* Send request */
1036 wbc_status = wbcRequestResponse(ctx, cmd,
1037 &request,
1038 &response);
1039 if (WBC_ERROR_IS_OK(wbc_status)) {
1040 goto done;
1043 /* Take the response above and return it to the caller */
1045 if (response.data.auth.nt_status != 0) {
1046 if (error) {
1047 wbc_status = wbc_create_error_info(&response,
1048 error);
1049 BAIL_ON_WBC_ERROR(wbc_status);
1054 if (policy) {
1055 wbc_status = wbc_create_password_policy_info(&response,
1056 policy);
1057 BAIL_ON_WBC_ERROR(wbc_status);
1060 if (reject_reason) {
1061 *reject_reason = response.data.auth.reject_reason;
1064 wbc_status = WBC_ERR_PWD_CHANGE_FAILED;
1065 BAIL_ON_WBC_ERROR(wbc_status);
1067 done:
1068 return wbc_status;
1071 wbcErr wbcChangeUserPasswordEx(const struct wbcChangePasswordParams *params,
1072 struct wbcAuthErrorInfo **error,
1073 enum wbcPasswordChangeRejectReason *reject_reason,
1074 struct wbcUserPasswordPolicyInfo **policy)
1076 return wbcCtxChangeUserPasswordEx(NULL, params, error,
1077 reject_reason, policy);
1080 /* Change a password for a user */
1081 wbcErr wbcCtxChangeUserPassword(struct wbcContext *ctx,
1082 const char *username,
1083 const char *old_password,
1084 const char *new_password)
1086 wbcErr wbc_status = WBC_ERR_SUCCESS;
1087 struct wbcChangePasswordParams params;
1089 ZERO_STRUCT(params);
1091 params.account_name = username;
1092 params.level = WBC_CHANGE_PASSWORD_LEVEL_PLAIN;
1093 params.old_password.plaintext = old_password;
1094 params.new_password.plaintext = new_password;
1096 wbc_status = wbcCtxChangeUserPasswordEx(ctx, &params,
1097 NULL,
1098 NULL,
1099 NULL);
1100 BAIL_ON_WBC_ERROR(wbc_status);
1102 done:
1103 return wbc_status;
1106 wbcErr wbcChangeUserPassword(const char *username,
1107 const char *old_password,
1108 const char *new_password)
1110 return wbcCtxChangeUserPassword(NULL, username,
1111 old_password, new_password);
1114 /* Logon a User */
1115 wbcErr wbcCtxLogonUser(struct wbcContext *ctx,
1116 const struct wbcLogonUserParams *params,
1117 struct wbcLogonUserInfo **info,
1118 struct wbcAuthErrorInfo **error,
1119 struct wbcUserPasswordPolicyInfo **policy)
1121 wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
1122 struct winbindd_request request;
1123 struct winbindd_response response;
1124 uint32_t i;
1126 ZERO_STRUCT(request);
1127 ZERO_STRUCT(response);
1129 if (info) {
1130 *info = NULL;
1132 if (error) {
1133 *error = NULL;
1135 if (policy) {
1136 *policy = NULL;
1139 if (!params) {
1140 wbc_status = WBC_ERR_INVALID_PARAM;
1141 BAIL_ON_WBC_ERROR(wbc_status);
1144 if (!params->username) {
1145 wbc_status = WBC_ERR_INVALID_PARAM;
1146 BAIL_ON_WBC_ERROR(wbc_status);
1149 if ((params->num_blobs > 0) && (params->blobs == NULL)) {
1150 wbc_status = WBC_ERR_INVALID_PARAM;
1151 BAIL_ON_WBC_ERROR(wbc_status);
1153 if ((params->num_blobs == 0) && (params->blobs != NULL)) {
1154 wbc_status = WBC_ERR_INVALID_PARAM;
1155 BAIL_ON_WBC_ERROR(wbc_status);
1158 /* Initialize request */
1160 request.flags = WBFLAG_PAM_INFO3_TEXT |
1161 WBFLAG_PAM_USER_SESSION_KEY |
1162 WBFLAG_PAM_LMKEY;
1164 if (!params->password) {
1165 wbc_status = WBC_ERR_INVALID_PARAM;
1166 BAIL_ON_WBC_ERROR(wbc_status);
1169 strncpy(request.data.auth.user,
1170 params->username,
1171 sizeof(request.data.auth.user)-1);
1173 strncpy(request.data.auth.pass,
1174 params->password,
1175 sizeof(request.data.auth.pass)-1);
1177 for (i=0; i<params->num_blobs; i++) {
1179 if (strcasecmp(params->blobs[i].name, "krb5_cc_type") == 0) {
1180 if (params->blobs[i].blob.data) {
1181 strncpy(request.data.auth.krb5_cc_type,
1182 (const char *)params->blobs[i].blob.data,
1183 sizeof(request.data.auth.krb5_cc_type) - 1);
1185 continue;
1188 if (strcasecmp(params->blobs[i].name, "user_uid") == 0) {
1189 if (params->blobs[i].blob.data) {
1190 memcpy(&request.data.auth.uid,
1191 params->blobs[i].blob.data,
1192 MIN(sizeof(request.data.auth.uid),
1193 params->blobs[i].blob.length));
1195 continue;
1198 if (strcasecmp(params->blobs[i].name, "flags") == 0) {
1199 if (params->blobs[i].blob.data) {
1200 uint32_t flags;
1201 memcpy(&flags,
1202 params->blobs[i].blob.data,
1203 MIN(sizeof(flags),
1204 params->blobs[i].blob.length));
1205 request.flags |= flags;
1207 continue;
1210 if (strcasecmp(params->blobs[i].name, "membership_of") == 0) {
1211 if (params->blobs[i].blob.data &&
1212 params->blobs[i].blob.data[0] > 0) {
1213 strncpy(request.data.auth.require_membership_of_sid,
1214 (const char *)params->blobs[i].blob.data,
1215 sizeof(request.data.auth.require_membership_of_sid) - 1);
1217 continue;
1221 wbc_status = wbcRequestResponse(ctx, WINBINDD_PAM_AUTH,
1222 &request,
1223 &response);
1225 if (response.data.auth.nt_status != 0) {
1226 if (error) {
1227 wbc_status = wbc_create_error_info(&response,
1228 error);
1229 BAIL_ON_WBC_ERROR(wbc_status);
1232 wbc_status = WBC_ERR_AUTH_ERROR;
1233 BAIL_ON_WBC_ERROR(wbc_status);
1235 BAIL_ON_WBC_ERROR(wbc_status);
1237 if (info) {
1238 wbc_status = wbc_create_logon_info(&response,
1239 info);
1240 BAIL_ON_WBC_ERROR(wbc_status);
1243 if (policy) {
1244 wbc_status = wbc_create_password_policy_info(&response,
1245 policy);
1246 BAIL_ON_WBC_ERROR(wbc_status);
1249 done:
1250 winbindd_free_response(&response);
1252 return wbc_status;
1255 wbcErr wbcLogonUser(const struct wbcLogonUserParams *params,
1256 struct wbcLogonUserInfo **info,
1257 struct wbcAuthErrorInfo **error,
1258 struct wbcUserPasswordPolicyInfo **policy)
1260 return wbcCtxLogonUser(NULL, params, info, error, policy);
1263 static void wbcCredentialCacheInfoDestructor(void *ptr)
1265 struct wbcCredentialCacheInfo *i =
1266 (struct wbcCredentialCacheInfo *)ptr;
1267 wbcFreeMemory(i->blobs);
1270 /* Authenticate a user with cached credentials */
1271 wbcErr wbcCtxCredentialCache(struct wbcContext *ctx,
1272 struct wbcCredentialCacheParams *params,
1273 struct wbcCredentialCacheInfo **info,
1274 struct wbcAuthErrorInfo **error)
1276 wbcErr status = WBC_ERR_UNKNOWN_FAILURE;
1277 struct wbcCredentialCacheInfo *result = NULL;
1278 struct winbindd_request request;
1279 struct winbindd_response response;
1280 struct wbcNamedBlob *initial_blob = NULL;
1281 struct wbcNamedBlob *challenge_blob = NULL;
1282 size_t i;
1284 ZERO_STRUCT(request);
1285 ZERO_STRUCT(response);
1287 *info = NULL;
1289 if (error != NULL) {
1290 *error = NULL;
1292 if ((params == NULL)
1293 || (params->account_name == NULL)
1294 || (params->level != WBC_CREDENTIAL_CACHE_LEVEL_NTLMSSP)) {
1295 status = WBC_ERR_INVALID_PARAM;
1296 goto fail;
1299 for (i=0; i<params->num_blobs; i++) {
1301 * Older callers may used to provide the NEGOTIATE request
1302 * as "initial_blob", but it was completely ignored by winbindd.
1304 * So we keep ignoring it.
1306 * A new callers that is capable to support "new_spnego",
1307 * will provide the NEGOTIATE request as "negotiate_blob"
1308 * instead.
1310 if (strcasecmp(params->blobs[i].name, "negotiate_blob") == 0) {
1311 if (initial_blob != NULL) {
1312 status = WBC_ERR_INVALID_PARAM;
1313 goto fail;
1315 initial_blob = &params->blobs[i];
1316 continue;
1318 if (strcasecmp(params->blobs[i].name, "challenge_blob") == 0) {
1319 if (challenge_blob != NULL) {
1320 status = WBC_ERR_INVALID_PARAM;
1321 goto fail;
1323 challenge_blob = &params->blobs[i];
1324 continue;
1328 if (params->domain_name != NULL) {
1329 status = wbcRequestResponse(ctx, WINBINDD_INFO,
1330 NULL, &response);
1331 if (!WBC_ERROR_IS_OK(status)) {
1332 goto fail;
1334 snprintf(request.data.ccache_ntlm_auth.user,
1335 sizeof(request.data.ccache_ntlm_auth.user)-1,
1336 "%s%c%s", params->domain_name,
1337 response.data.info.winbind_separator,
1338 params->account_name);
1339 } else {
1340 strncpy(request.data.ccache_ntlm_auth.user,
1341 params->account_name,
1342 sizeof(request.data.ccache_ntlm_auth.user)-1);
1344 request.data.ccache_ntlm_auth.uid = getuid();
1346 request.data.ccache_ntlm_auth.initial_blob_len = 0;
1347 request.data.ccache_ntlm_auth.challenge_blob_len = 0;
1348 request.extra_len = 0;
1350 if (initial_blob != NULL) {
1351 request.data.ccache_ntlm_auth.initial_blob_len =
1352 initial_blob->blob.length;
1353 request.extra_len += initial_blob->blob.length;
1355 if (challenge_blob != NULL) {
1356 request.data.ccache_ntlm_auth.challenge_blob_len =
1357 challenge_blob->blob.length;
1358 request.extra_len += challenge_blob->blob.length;
1361 if (request.extra_len != 0) {
1362 request.extra_data.data = (char *)malloc(request.extra_len);
1363 if (request.extra_data.data == NULL) {
1364 status = WBC_ERR_NO_MEMORY;
1365 goto fail;
1368 if (initial_blob != NULL) {
1369 memcpy(request.extra_data.data,
1370 initial_blob->blob.data, initial_blob->blob.length);
1372 if (challenge_blob != NULL) {
1373 memcpy(request.extra_data.data
1374 + request.data.ccache_ntlm_auth.initial_blob_len,
1375 challenge_blob->blob.data,
1376 challenge_blob->blob.length);
1379 status = wbcRequestResponse(ctx, WINBINDD_CCACHE_NTLMAUTH,
1380 &request, &response);
1381 if (!WBC_ERROR_IS_OK(status)) {
1382 goto fail;
1385 result = (struct wbcCredentialCacheInfo *)wbcAllocateMemory(
1386 1, sizeof(struct wbcCredentialCacheInfo),
1387 wbcCredentialCacheInfoDestructor);
1388 if (result == NULL) {
1389 status = WBC_ERR_NO_MEMORY;
1390 goto fail;
1392 result->num_blobs = 0;
1393 result->blobs = NULL;
1394 status = wbcAddNamedBlob(&result->num_blobs, &result->blobs,
1395 "auth_blob", 0,
1396 (uint8_t *)response.extra_data.data,
1397 response.data.ccache_ntlm_auth.auth_blob_len);
1398 if (!WBC_ERROR_IS_OK(status)) {
1399 goto fail;
1401 status = wbcAddNamedBlob(
1402 &result->num_blobs, &result->blobs, "session_key", 0,
1403 response.data.ccache_ntlm_auth.session_key,
1404 sizeof(response.data.ccache_ntlm_auth.session_key));
1405 if (!WBC_ERROR_IS_OK(status)) {
1406 goto fail;
1408 if (response.data.ccache_ntlm_auth.new_spnego) {
1409 status = wbcAddNamedBlob(
1410 &result->num_blobs, &result->blobs, "new_spnego", 0,
1411 &response.data.ccache_ntlm_auth.new_spnego,
1412 sizeof(response.data.ccache_ntlm_auth.new_spnego));
1413 if (!WBC_ERROR_IS_OK(status)) {
1414 goto fail;
1418 *info = result;
1419 result = NULL;
1420 status = WBC_ERR_SUCCESS;
1421 fail:
1422 free(request.extra_data.data);
1423 winbindd_free_response(&response);
1424 wbcFreeMemory(result);
1425 return status;
1428 wbcErr wbcCredentialCache(struct wbcCredentialCacheParams *params,
1429 struct wbcCredentialCacheInfo **info,
1430 struct wbcAuthErrorInfo **error)
1432 return wbcCtxCredentialCache(NULL, params, info, error);
1435 /* Authenticate a user with cached credentials */
1436 wbcErr wbcCtxCredentialSave(struct wbcContext *ctx,
1437 const char *user, const char *password)
1439 struct winbindd_request request;
1440 struct winbindd_response response;
1442 ZERO_STRUCT(request);
1443 ZERO_STRUCT(response);
1445 strncpy(request.data.ccache_save.user, user,
1446 sizeof(request.data.ccache_save.user)-1);
1447 strncpy(request.data.ccache_save.pass, password,
1448 sizeof(request.data.ccache_save.pass)-1);
1449 request.data.ccache_save.uid = getuid();
1451 return wbcRequestResponse(ctx, WINBINDD_CCACHE_SAVE, &request, &response);
1454 wbcErr wbcCredentialSave(const char *user, const char *password)
1456 return wbcCtxCredentialSave(NULL, user, password);