2 Unix SMB/CIFS implementation.
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 */
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
;
39 params
.account_name
= username
;
40 params
.level
= WBC_AUTH_USER_LEVEL_PLAIN
;
41 params
.password
.plaintext
= password
;
43 wbc_status
= wbcCtxAuthenticateUserEx(ctx
, ¶ms
, NULL
, NULL
);
44 BAIL_ON_WBC_ERROR(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
) {
63 s
->sid
.sub_auths
[s
->sid
.num_auths
++] = rid
;
68 static void wbcAuthUserInfoDestructor(void *ptr
)
70 struct wbcAuthUserInfo
*i
= (struct wbcAuthUserInfo
*)ptr
;
71 free(i
->account_name
);
72 free(i
->user_principal
);
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
);
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
;
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
);
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
);
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
);
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
,
159 BAIL_ON_WBC_ERROR(wbc_status
);
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
;
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
;
175 p
= (char *)resp
->extra_data
.data
;
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
++) {
186 char *e
= strchr(p
, '\n');
188 wbc_status
= WBC_ERR_INVALID_RESPONSE
;
189 BAIL_ON_WBC_ERROR(wbc_status
);
194 ret
= sscanf(s
, "0x%08X:0x%08X", &rid
, &attrs
);
196 wbc_status
= WBC_ERR_INVALID_RESPONSE
;
197 BAIL_ON_WBC_ERROR(wbc_status
);
200 if (!sid_attr_compose(&i
->sids
[sn
], &domain_sid
,
202 wbc_status
= WBC_ERR_INVALID_SID
;
208 for (j
=0; j
< resp
->data
.auth
.info3
.num_other_sids
; j
++) {
213 char *e
= strchr(p
, '\n');
215 wbc_status
= WBC_ERR_INVALID_RESPONSE
;
216 BAIL_ON_WBC_ERROR(wbc_status
);
223 wbc_status
= WBC_ERR_INVALID_RESPONSE
;
224 BAIL_ON_WBC_ERROR(wbc_status
);
229 ret
= sscanf(a
, "0x%08X",
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
;
252 static void wbcAuthErrorInfoDestructor(void *ptr
)
254 struct wbcAuthErrorInfo
*e
= (struct wbcAuthErrorInfo
*)ptr
;
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
);
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
;
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
,
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
,
347 (uint8_t *)resp
->data
.auth
.unix_username
,
348 strlen(resp
->data
.auth
.unix_username
)+1);
349 BAIL_ON_WBC_ERROR(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
;
368 struct winbindd_request request
;
369 struct winbindd_response response
;
371 ZERO_STRUCT(request
);
372 ZERO_STRUCT(response
);
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
|
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,
416 sep_response
.data
.info
.winbind_separator
,
417 params
->account_name
);
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);
429 case WBC_AUTH_USER_LEVEL_HASH
:
430 wbc_status
= WBC_ERR_NOT_IMPLEMENTED
;
431 BAIL_ON_WBC_ERROR(wbc_status
);
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
|
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
,
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(
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
);
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
;
529 wbc_status
= WBC_ERR_INVALID_PARAM
;
530 BAIL_ON_WBC_ERROR(wbc_status
);
534 request
.flags
|= params
->flags
;
537 if (cmd
== WINBINDD_PAM_AUTH_CRAP
) {
538 wbc_status
= wbcRequestResponsePriv(ctx
, cmd
,
539 &request
, &response
);
541 wbc_status
= wbcRequestResponse(ctx
, cmd
,
542 &request
, &response
);
544 if (response
.data
.auth
.nt_status
!= 0) {
546 wbc_status
= wbc_create_error_info(&response
,
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
);
557 wbc_status
= wbc_create_auth_info(&response
, info
);
558 BAIL_ON_WBC_ERROR(wbc_status
);
562 winbindd_free_response(&response
);
564 free(request
.extra_data
.data
);
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
);
588 strncpy(request
.domain_name
, domain
,
589 sizeof(request
.domain_name
)-1);
594 wbc_status
= wbcRequestResponsePriv(ctx
, WINBINDD_CHECK_MACHACC
,
595 &request
, &response
);
596 if (response
.data
.auth
.nt_status
!= 0) {
598 wbc_status
= wbc_create_error_info(&response
,
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
);
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
);
630 strncpy(request
.domain_name
, domain
,
631 sizeof(request
.domain_name
)-1);
636 wbc_status
= wbcRequestResponsePriv(ctx
, WINBINDD_CHANGE_MACHACC
,
637 &request
, &response
);
638 if (response
.data
.auth
.nt_status
!= 0) {
640 wbc_status
= wbc_create_error_info(&response
,
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
);
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
);
690 strncpy(request
.domain_name
, domain
,
691 sizeof(request
.domain_name
)-1);
696 wbc_status
= wbcRequestResponse(ctx
, WINBINDD_PING_DC
,
700 if (dcname
&& response
.extra_data
.data
) {
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) {
712 wbc_status
= wbc_create_error_info(&response
,
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
);
726 wbcErr
wbcPingDc2(const char *domain
, struct wbcAuthErrorInfo
**error
,
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
;
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);
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
)));
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
)));
798 wbc_status
= wbcRequestResponse(ctx
, WINBINDD_PAM_LOGOFF
,
802 /* Take the response above and return it to the caller */
803 if (response
.data
.auth
.nt_status
!= 0) {
805 wbc_status
= wbc_create_error_info(&response
,
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
);
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
;
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
;
849 strncpy(request
.data
.logoff
.krb5ccname
, ccfilename
,
850 sizeof(request
.data
.logoff
.krb5ccname
)-1);
855 wbc_status
= wbcRequestResponse(ctx
, WINBINDD_PAM_LOGOFF
,
859 /* Take the response above and return it to the caller */
865 wbcErr
wbcLogoffUser(const char *username
,
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
;
886 if (!params
->account_name
) {
887 wbc_status
= WBC_ERR_INVALID_PARAM
;
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
;
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);
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
;
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
;
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
;
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
;
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
;
963 if (params
->new_password
.response
.lm_length
&&
964 !params
->new_password
.response
.lm_data
) {
965 wbc_status
= WBC_ERR_INVALID_PARAM
;
969 if (params
->new_password
.response
.lm_length
== 0 &&
970 params
->new_password
.response
.lm_data
) {
971 wbc_status
= WBC_ERR_INVALID_PARAM
;
975 if (params
->new_password
.response
.nt_length
&&
976 !params
->new_password
.response
.nt_data
) {
977 wbc_status
= WBC_ERR_INVALID_PARAM
;
981 if (params
->new_password
.response
.nt_length
== 0 &&
982 params
->new_password
.response
.nt_data
) {
983 wbc_status
= WBC_ERR_INVALID_PARAM
;
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
,
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
);
1029 wbc_status
= WBC_ERR_INVALID_PARAM
;
1036 wbc_status
= wbcRequestResponse(ctx
, cmd
,
1039 if (WBC_ERROR_IS_OK(wbc_status
)) {
1043 /* Take the response above and return it to the caller */
1045 if (response
.data
.auth
.nt_status
!= 0) {
1047 wbc_status
= wbc_create_error_info(&response
,
1049 BAIL_ON_WBC_ERROR(wbc_status
);
1055 wbc_status
= wbc_create_password_policy_info(&response
,
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
);
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
, ¶ms
,
1100 BAIL_ON_WBC_ERROR(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
);
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
;
1126 ZERO_STRUCT(request
);
1127 ZERO_STRUCT(response
);
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
|
1164 if (!params
->password
) {
1165 wbc_status
= WBC_ERR_INVALID_PARAM
;
1166 BAIL_ON_WBC_ERROR(wbc_status
);
1169 strncpy(request
.data
.auth
.user
,
1171 sizeof(request
.data
.auth
.user
)-1);
1173 strncpy(request
.data
.auth
.pass
,
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);
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
));
1198 if (strcasecmp(params
->blobs
[i
].name
, "flags") == 0) {
1199 if (params
->blobs
[i
].blob
.data
) {
1202 params
->blobs
[i
].blob
.data
,
1204 params
->blobs
[i
].blob
.length
));
1205 request
.flags
|= flags
;
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);
1221 wbc_status
= wbcRequestResponse(ctx
, WINBINDD_PAM_AUTH
,
1225 if (response
.data
.auth
.nt_status
!= 0) {
1227 wbc_status
= wbc_create_error_info(&response
,
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
);
1238 wbc_status
= wbc_create_logon_info(&response
,
1240 BAIL_ON_WBC_ERROR(wbc_status
);
1244 wbc_status
= wbc_create_password_policy_info(&response
,
1246 BAIL_ON_WBC_ERROR(wbc_status
);
1250 winbindd_free_response(&response
);
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
;
1284 ZERO_STRUCT(request
);
1285 ZERO_STRUCT(response
);
1289 if (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
;
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"
1310 if (strcasecmp(params
->blobs
[i
].name
, "negotiate_blob") == 0) {
1311 if (initial_blob
!= NULL
) {
1312 status
= WBC_ERR_INVALID_PARAM
;
1315 initial_blob
= ¶ms
->blobs
[i
];
1318 if (strcasecmp(params
->blobs
[i
].name
, "challenge_blob") == 0) {
1319 if (challenge_blob
!= NULL
) {
1320 status
= WBC_ERR_INVALID_PARAM
;
1323 challenge_blob
= ¶ms
->blobs
[i
];
1328 if (params
->domain_name
!= NULL
) {
1329 status
= wbcRequestResponse(ctx
, WINBINDD_INFO
,
1331 if (!WBC_ERROR_IS_OK(status
)) {
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
);
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
;
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
)) {
1385 result
= (struct wbcCredentialCacheInfo
*)wbcAllocateMemory(
1386 1, sizeof(struct wbcCredentialCacheInfo
),
1387 wbcCredentialCacheInfoDestructor
);
1388 if (result
== NULL
) {
1389 status
= WBC_ERR_NO_MEMORY
;
1392 result
->num_blobs
= 0;
1393 result
->blobs
= NULL
;
1394 status
= wbcAddNamedBlob(&result
->num_blobs
, &result
->blobs
,
1396 (uint8_t *)response
.extra_data
.data
,
1397 response
.data
.ccache_ntlm_auth
.auth_blob_len
);
1398 if (!WBC_ERROR_IS_OK(status
)) {
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
)) {
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
)) {
1420 status
= WBC_ERR_SUCCESS
;
1422 free(request
.extra_data
.data
);
1423 winbindd_free_response(&response
);
1424 wbcFreeMemory(result
);
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
);