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 */
32 wbcErr
wbcCtxAuthenticateUser(struct wbcContext
*ctx
,
33 const char *username
, const char *password
)
35 wbcErr wbc_status
= WBC_ERR_SUCCESS
;
36 struct wbcAuthUserParams params
;
40 params
.account_name
= username
;
41 params
.level
= WBC_AUTH_USER_LEVEL_PLAIN
;
42 params
.password
.plaintext
= password
;
44 wbc_status
= wbcCtxAuthenticateUserEx(ctx
, ¶ms
, NULL
, NULL
);
45 BAIL_ON_WBC_ERROR(wbc_status
);
52 wbcErr
wbcAuthenticateUser(const char *username
, const char *password
)
54 return wbcCtxAuthenticateUser(NULL
, username
, password
);
57 static bool sid_attr_compose(struct wbcSidWithAttr
*s
,
58 const struct wbcDomainSid
*d
,
59 uint32_t rid
, uint32_t attr
)
61 if (d
->num_auths
>= WBC_MAXSUBAUTHS
) {
65 s
->sid
.sub_auths
[s
->sid
.num_auths
++] = rid
;
70 static void wbcAuthUserInfoDestructor(void *ptr
)
72 struct wbcAuthUserInfo
*i
= (struct wbcAuthUserInfo
*)ptr
;
73 free(i
->account_name
);
74 free(i
->user_principal
);
77 free(i
->dns_domain_name
);
78 free(i
->logon_server
);
79 free(i
->logon_script
);
80 free(i
->profile_path
);
81 free(i
->home_directory
);
86 static wbcErr
wbc_create_auth_info(const struct winbindd_response
*resp
,
87 struct wbcAuthUserInfo
**_i
)
89 wbcErr wbc_status
= WBC_ERR_SUCCESS
;
90 struct wbcAuthUserInfo
*i
;
91 struct wbcDomainSid domain_sid
;
96 i
= (struct wbcAuthUserInfo
*)wbcAllocateMemory(
97 1, sizeof(struct wbcAuthUserInfo
),
98 wbcAuthUserInfoDestructor
);
99 BAIL_ON_PTR_ERROR(i
, wbc_status
);
101 i
->user_flags
= resp
->data
.auth
.info3
.user_flgs
;
103 i
->account_name
= strdup(resp
->data
.auth
.info3
.user_name
);
104 BAIL_ON_PTR_ERROR(i
->account_name
, wbc_status
);
105 if (resp
->data
.auth
.validation_level
== 6) {
106 i
->user_principal
= strdup(resp
->data
.auth
.info6
.principal_name
);
107 BAIL_ON_PTR_ERROR(i
->user_principal
, wbc_status
);
109 i
->user_principal
= NULL
;
111 i
->full_name
= strdup(resp
->data
.auth
.info3
.full_name
);
112 BAIL_ON_PTR_ERROR(i
->full_name
, wbc_status
);
113 i
->domain_name
= strdup(resp
->data
.auth
.info3
.logon_dom
);
114 BAIL_ON_PTR_ERROR(i
->domain_name
, wbc_status
);
115 if (resp
->data
.auth
.validation_level
== 6) {
116 i
->dns_domain_name
= strdup(resp
->data
.auth
.info6
.dns_domainname
);
117 BAIL_ON_PTR_ERROR(i
->dns_domain_name
, wbc_status
);
119 i
->dns_domain_name
= NULL
;
122 i
->acct_flags
= resp
->data
.auth
.info3
.acct_flags
;
123 memcpy(i
->user_session_key
,
124 resp
->data
.auth
.user_session_key
,
125 sizeof(i
->user_session_key
));
126 memcpy(i
->lm_session_key
,
127 resp
->data
.auth
.first_8_lm_hash
,
128 sizeof(i
->lm_session_key
));
130 i
->logon_count
= resp
->data
.auth
.info3
.logon_count
;
131 i
->bad_password_count
= resp
->data
.auth
.info3
.bad_pw_count
;
133 i
->logon_time
= resp
->data
.auth
.info3
.logon_time
;
134 i
->logoff_time
= resp
->data
.auth
.info3
.logoff_time
;
135 i
->kickoff_time
= resp
->data
.auth
.info3
.kickoff_time
;
136 i
->pass_last_set_time
= resp
->data
.auth
.info3
.pass_last_set_time
;
137 i
->pass_can_change_time
= resp
->data
.auth
.info3
.pass_can_change_time
;
138 i
->pass_must_change_time
= resp
->data
.auth
.info3
.pass_must_change_time
;
140 i
->logon_server
= strdup(resp
->data
.auth
.info3
.logon_srv
);
141 BAIL_ON_PTR_ERROR(i
->logon_server
, wbc_status
);
142 i
->logon_script
= strdup(resp
->data
.auth
.info3
.logon_script
);
143 BAIL_ON_PTR_ERROR(i
->logon_script
, wbc_status
);
144 i
->profile_path
= strdup(resp
->data
.auth
.info3
.profile_path
);
145 BAIL_ON_PTR_ERROR(i
->profile_path
, wbc_status
);
146 i
->home_directory
= strdup(resp
->data
.auth
.info3
.home_dir
);
147 BAIL_ON_PTR_ERROR(i
->home_directory
, wbc_status
);
148 i
->home_drive
= strdup(resp
->data
.auth
.info3
.dir_drive
);
149 BAIL_ON_PTR_ERROR(i
->home_drive
, wbc_status
);
152 i
->num_sids
+= resp
->data
.auth
.info3
.num_groups
;
153 i
->num_sids
+= resp
->data
.auth
.info3
.num_other_sids
;
155 i
->sids
= (struct wbcSidWithAttr
*)calloc(
156 sizeof(struct wbcSidWithAttr
), i
->num_sids
);
157 BAIL_ON_PTR_ERROR(i
->sids
, wbc_status
);
159 wbc_status
= wbcStringToSid(resp
->data
.auth
.info3
.dom_sid
,
161 BAIL_ON_WBC_ERROR(wbc_status
);
164 if (!sid_attr_compose(&i
->sids
[sn
], &domain_sid
,
165 resp
->data
.auth
.info3
.user_rid
, 0)) {
166 wbc_status
= WBC_ERR_INVALID_SID
;
170 if (!sid_attr_compose(&i
->sids
[sn
], &domain_sid
,
171 resp
->data
.auth
.info3
.group_rid
, 0)) {
172 wbc_status
= WBC_ERR_INVALID_SID
;
177 p
= (char *)resp
->extra_data
.data
;
179 wbc_status
= WBC_ERR_INVALID_RESPONSE
;
180 BAIL_ON_WBC_ERROR(wbc_status
);
183 for (j
=0; j
< resp
->data
.auth
.info3
.num_groups
; j
++) {
188 char *e
= strchr(p
, '\n');
190 wbc_status
= WBC_ERR_INVALID_RESPONSE
;
191 BAIL_ON_WBC_ERROR(wbc_status
);
196 ret
= sscanf(s
, "0x%08X:0x%08X", &rid
, &attrs
);
198 wbc_status
= WBC_ERR_INVALID_RESPONSE
;
199 BAIL_ON_WBC_ERROR(wbc_status
);
202 if (!sid_attr_compose(&i
->sids
[sn
], &domain_sid
,
204 wbc_status
= WBC_ERR_INVALID_SID
;
210 for (j
=0; j
< resp
->data
.auth
.info3
.num_other_sids
; j
++) {
215 char *e
= strchr(p
, '\n');
217 wbc_status
= WBC_ERR_INVALID_RESPONSE
;
218 BAIL_ON_WBC_ERROR(wbc_status
);
225 wbc_status
= WBC_ERR_INVALID_RESPONSE
;
226 BAIL_ON_WBC_ERROR(wbc_status
);
231 ret
= sscanf(a
, "0x%08X",
234 wbc_status
= WBC_ERR_INVALID_RESPONSE
;
235 BAIL_ON_WBC_ERROR(wbc_status
);
238 wbc_status
= wbcStringToSid(s
, &i
->sids
[sn
].sid
);
239 BAIL_ON_WBC_ERROR(wbc_status
);
241 i
->sids
[sn
].attributes
= attrs
;
254 static void wbcAuthErrorInfoDestructor(void *ptr
)
256 struct wbcAuthErrorInfo
*e
= (struct wbcAuthErrorInfo
*)ptr
;
258 free(e
->display_string
);
261 static wbcErr
wbc_create_error_info(const struct winbindd_response
*resp
,
262 struct wbcAuthErrorInfo
**_e
)
264 wbcErr wbc_status
= WBC_ERR_SUCCESS
;
265 struct wbcAuthErrorInfo
*e
;
267 e
= (struct wbcAuthErrorInfo
*)wbcAllocateMemory(
268 1, sizeof(struct wbcAuthErrorInfo
),
269 wbcAuthErrorInfoDestructor
);
270 BAIL_ON_PTR_ERROR(e
, wbc_status
);
272 e
->nt_status
= resp
->data
.auth
.nt_status
;
273 e
->pam_error
= resp
->data
.auth
.pam_error
;
274 e
->authoritative
= resp
->data
.auth
.authoritative
;
275 e
->nt_string
= strdup(resp
->data
.auth
.nt_status_string
);
276 BAIL_ON_PTR_ERROR(e
->nt_string
, wbc_status
);
278 e
->display_string
= strdup(resp
->data
.auth
.error_string
);
279 BAIL_ON_PTR_ERROR(e
->display_string
, wbc_status
);
289 static wbcErr
wbc_create_password_policy_info(const struct winbindd_response
*resp
,
290 struct wbcUserPasswordPolicyInfo
**_i
)
292 wbcErr wbc_status
= WBC_ERR_SUCCESS
;
293 struct wbcUserPasswordPolicyInfo
*i
;
295 i
= (struct wbcUserPasswordPolicyInfo
*)wbcAllocateMemory(
296 1, sizeof(struct wbcUserPasswordPolicyInfo
), NULL
);
297 BAIL_ON_PTR_ERROR(i
, wbc_status
);
299 i
->min_passwordage
= resp
->data
.auth
.policy
.min_passwordage
;
300 i
->min_length_password
= resp
->data
.auth
.policy
.min_length_password
;
301 i
->password_history
= resp
->data
.auth
.policy
.password_history
;
302 i
->password_properties
= resp
->data
.auth
.policy
.password_properties
;
303 i
->expire
= resp
->data
.auth
.policy
.expire
;
313 static void wbcLogonUserInfoDestructor(void *ptr
)
315 struct wbcLogonUserInfo
*i
= (struct wbcLogonUserInfo
*)ptr
;
316 wbcFreeMemory(i
->info
);
317 wbcFreeMemory(i
->blobs
);
320 static wbcErr
wbc_create_logon_info(struct winbindd_response
*resp
,
321 struct wbcLogonUserInfo
**_i
)
323 wbcErr wbc_status
= WBC_ERR_SUCCESS
;
324 struct wbcLogonUserInfo
*i
;
326 i
= (struct wbcLogonUserInfo
*)wbcAllocateMemory(
327 1, sizeof(struct wbcLogonUserInfo
),
328 wbcLogonUserInfoDestructor
);
329 BAIL_ON_PTR_ERROR(i
, wbc_status
);
331 wbc_status
= wbc_create_auth_info(resp
, &i
->info
);
332 BAIL_ON_WBC_ERROR(wbc_status
);
334 if (resp
->data
.auth
.krb5ccname
[0] != '\0') {
335 wbc_status
= wbcAddNamedBlob(&i
->num_blobs
,
339 (uint8_t *)resp
->data
.auth
.krb5ccname
,
340 strlen(resp
->data
.auth
.krb5ccname
)+1);
341 BAIL_ON_WBC_ERROR(wbc_status
);
344 if (resp
->data
.auth
.unix_username
[0] != '\0') {
345 wbc_status
= wbcAddNamedBlob(&i
->num_blobs
,
349 (uint8_t *)resp
->data
.auth
.unix_username
,
350 strlen(resp
->data
.auth
.unix_username
)+1);
351 BAIL_ON_WBC_ERROR(wbc_status
);
362 /* Authenticate with more detailed information */
364 wbcErr
wbcCtxAuthenticateUserEx(struct wbcContext
*ctx
,
365 const struct wbcAuthUserParams
*params
,
366 struct wbcAuthUserInfo
**info
,
367 struct wbcAuthErrorInfo
**error
)
369 wbcErr wbc_status
= WBC_ERR_UNKNOWN_FAILURE
;
371 struct winbindd_request request
;
372 struct winbindd_response response
;
374 ZERO_STRUCT(request
);
375 ZERO_STRUCT(response
);
382 wbc_status
= WBC_ERR_INVALID_PARAM
;
383 BAIL_ON_WBC_ERROR(wbc_status
);
386 if (params
->level
!= WBC_AUTH_USER_LEVEL_PAC
&& !params
->account_name
) {
387 wbc_status
= WBC_ERR_INVALID_PARAM
;
388 BAIL_ON_WBC_ERROR(wbc_status
);
391 /* Initialize request */
393 switch (params
->level
) {
394 case WBC_AUTH_USER_LEVEL_PLAIN
:
395 cmd
= WINBINDD_PAM_AUTH
;
396 request
.flags
= WBFLAG_PAM_INFO3_TEXT
|
397 WBFLAG_PAM_USER_SESSION_KEY
|
400 if (!params
->password
.plaintext
) {
401 wbc_status
= WBC_ERR_INVALID_PARAM
;
402 BAIL_ON_WBC_ERROR(wbc_status
);
405 if (params
->domain_name
&& params
->domain_name
[0]) {
406 /* We need to get the winbind separator :-( */
407 struct winbindd_response sep_response
;
409 ZERO_STRUCT(sep_response
);
411 wbc_status
= wbcRequestResponse(ctx
, WINBINDD_INFO
,
412 NULL
, &sep_response
);
413 BAIL_ON_WBC_ERROR(wbc_status
);
415 snprintf(request
.data
.auth
.user
,
416 sizeof(request
.data
.auth
.user
)-1,
419 sep_response
.data
.info
.winbind_separator
,
420 params
->account_name
);
422 strncpy(request
.data
.auth
.user
,
423 params
->account_name
,
424 sizeof(request
.data
.auth
.user
)-1);
427 strncpy(request
.data
.auth
.pass
,
428 params
->password
.plaintext
,
429 sizeof(request
.data
.auth
.pass
)-1);
432 case WBC_AUTH_USER_LEVEL_HASH
:
433 wbc_status
= WBC_ERR_NOT_IMPLEMENTED
;
434 BAIL_ON_WBC_ERROR(wbc_status
);
437 case WBC_AUTH_USER_LEVEL_RESPONSE
:
438 cmd
= WINBINDD_PAM_AUTH_CRAP
;
439 request
.flags
= WBFLAG_PAM_INFO3_TEXT
|
440 WBFLAG_PAM_USER_SESSION_KEY
|
443 if (params
->password
.response
.lm_length
&&
444 !params
->password
.response
.lm_data
) {
445 wbc_status
= WBC_ERR_INVALID_PARAM
;
446 BAIL_ON_WBC_ERROR(wbc_status
);
448 if (params
->password
.response
.lm_length
== 0 &&
449 params
->password
.response
.lm_data
) {
450 wbc_status
= WBC_ERR_INVALID_PARAM
;
451 BAIL_ON_WBC_ERROR(wbc_status
);
454 if (params
->password
.response
.nt_length
&&
455 !params
->password
.response
.nt_data
) {
456 wbc_status
= WBC_ERR_INVALID_PARAM
;
457 BAIL_ON_WBC_ERROR(wbc_status
);
459 if (params
->password
.response
.nt_length
== 0&&
460 params
->password
.response
.nt_data
) {
461 wbc_status
= WBC_ERR_INVALID_PARAM
;
462 BAIL_ON_WBC_ERROR(wbc_status
);
465 strncpy(request
.data
.auth_crap
.user
,
466 params
->account_name
,
467 sizeof(request
.data
.auth_crap
.user
)-1);
468 if (params
->domain_name
) {
469 strncpy(request
.data
.auth_crap
.domain
,
471 sizeof(request
.data
.auth_crap
.domain
)-1);
473 if (params
->workstation_name
) {
474 strncpy(request
.data
.auth_crap
.workstation
,
475 params
->workstation_name
,
476 sizeof(request
.data
.auth_crap
.workstation
)-1);
479 request
.data
.auth_crap
.logon_parameters
=
480 params
->parameter_control
;
482 memcpy(request
.data
.auth_crap
.chal
,
483 params
->password
.response
.challenge
,
484 sizeof(request
.data
.auth_crap
.chal
));
486 request
.data
.auth_crap
.lm_resp_len
=
487 MIN(params
->password
.response
.lm_length
,
488 sizeof(request
.data
.auth_crap
.lm_resp
));
489 if (params
->password
.response
.lm_data
) {
490 memcpy(request
.data
.auth_crap
.lm_resp
,
491 params
->password
.response
.lm_data
,
492 request
.data
.auth_crap
.lm_resp_len
);
494 request
.data
.auth_crap
.nt_resp_len
= params
->password
.response
.nt_length
;
495 if (params
->password
.response
.nt_length
> sizeof(request
.data
.auth_crap
.nt_resp
)) {
496 request
.flags
|= WBFLAG_BIG_NTLMV2_BLOB
;
497 request
.extra_len
= params
->password
.response
.nt_length
;
498 request
.extra_data
.data
= (char *)malloc(
500 if (request
.extra_data
.data
== NULL
) {
501 wbc_status
= WBC_ERR_NO_MEMORY
;
502 BAIL_ON_WBC_ERROR(wbc_status
);
504 memcpy(request
.extra_data
.data
,
505 params
->password
.response
.nt_data
,
506 request
.data
.auth_crap
.nt_resp_len
);
507 } else if (params
->password
.response
.nt_data
) {
508 memcpy(request
.data
.auth_crap
.nt_resp
,
509 params
->password
.response
.nt_data
,
510 request
.data
.auth_crap
.nt_resp_len
);
514 case WBC_AUTH_USER_LEVEL_PAC
:
515 cmd
= WINBINDD_PAM_AUTH_CRAP
;
516 request
.flags
= WBFLAG_PAM_AUTH_PAC
| WBFLAG_PAM_INFO3_TEXT
;
517 request
.extra_data
.data
= malloc(params
->password
.pac
.length
);
518 if (request
.extra_data
.data
== NULL
) {
519 wbc_status
= WBC_ERR_NO_MEMORY
;
520 BAIL_ON_WBC_ERROR(wbc_status
);
522 memcpy(request
.extra_data
.data
, params
->password
.pac
.data
,
523 params
->password
.pac
.length
);
524 request
.extra_len
= params
->password
.pac
.length
;
532 wbc_status
= WBC_ERR_INVALID_PARAM
;
533 BAIL_ON_WBC_ERROR(wbc_status
);
537 request
.flags
|= params
->flags
;
540 if (cmd
== WINBINDD_PAM_AUTH_CRAP
) {
541 wbc_status
= wbcRequestResponsePriv(ctx
, cmd
,
542 &request
, &response
);
544 wbc_status
= wbcRequestResponse(ctx
, cmd
,
545 &request
, &response
);
547 if (response
.data
.auth
.nt_status
!= 0) {
549 wbc_status
= wbc_create_error_info(&response
,
551 BAIL_ON_WBC_ERROR(wbc_status
);
554 wbc_status
= WBC_ERR_AUTH_ERROR
;
555 BAIL_ON_WBC_ERROR(wbc_status
);
557 BAIL_ON_WBC_ERROR(wbc_status
);
560 wbc_status
= wbc_create_auth_info(&response
, info
);
561 BAIL_ON_WBC_ERROR(wbc_status
);
565 winbindd_free_response(&response
);
567 free(request
.extra_data
.data
);
573 wbcErr
wbcAuthenticateUserEx(const struct wbcAuthUserParams
*params
,
574 struct wbcAuthUserInfo
**info
,
575 struct wbcAuthErrorInfo
**error
)
577 return wbcCtxAuthenticateUserEx(NULL
, params
, info
, error
);
580 /* Trigger a verification of the trust credentials of a specific domain */
582 wbcErr
wbcCtxCheckTrustCredentials(struct wbcContext
*ctx
, const char *domain
,
583 struct wbcAuthErrorInfo
**error
)
585 struct winbindd_request request
;
586 struct winbindd_response response
;
587 wbcErr wbc_status
= WBC_ERR_UNKNOWN_FAILURE
;
589 ZERO_STRUCT(request
);
590 ZERO_STRUCT(response
);
593 strncpy(request
.domain_name
, domain
,
594 sizeof(request
.domain_name
)-1);
599 wbc_status
= wbcRequestResponsePriv(ctx
, WINBINDD_CHECK_MACHACC
,
600 &request
, &response
);
601 if (response
.data
.auth
.nt_status
!= 0) {
603 wbc_status
= wbc_create_error_info(&response
,
605 BAIL_ON_WBC_ERROR(wbc_status
);
608 wbc_status
= WBC_ERR_AUTH_ERROR
;
609 BAIL_ON_WBC_ERROR(wbc_status
);
611 BAIL_ON_WBC_ERROR(wbc_status
);
618 wbcErr
wbcCheckTrustCredentials(const char *domain
,
619 struct wbcAuthErrorInfo
**error
)
621 return wbcCtxCheckTrustCredentials(NULL
, domain
, error
);
624 /* Trigger a change of the trust credentials for a specific domain */
626 wbcErr
wbcCtxChangeTrustCredentials(struct wbcContext
*ctx
, const char *domain
,
627 struct wbcAuthErrorInfo
**error
)
629 struct winbindd_request request
;
630 struct winbindd_response response
;
631 wbcErr wbc_status
= WBC_ERR_UNKNOWN_FAILURE
;
633 ZERO_STRUCT(request
);
634 ZERO_STRUCT(response
);
637 strncpy(request
.domain_name
, domain
,
638 sizeof(request
.domain_name
)-1);
643 wbc_status
= wbcRequestResponsePriv(ctx
, WINBINDD_CHANGE_MACHACC
,
644 &request
, &response
);
645 if (response
.data
.auth
.nt_status
!= 0) {
647 wbc_status
= wbc_create_error_info(&response
,
649 BAIL_ON_WBC_ERROR(wbc_status
);
652 wbc_status
= WBC_ERR_AUTH_ERROR
;
653 BAIL_ON_WBC_ERROR(wbc_status
);
655 BAIL_ON_WBC_ERROR(wbc_status
);
662 wbcErr
wbcChangeTrustCredentials(const char *domain
,
663 struct wbcAuthErrorInfo
**error
)
665 return wbcCtxChangeTrustCredentials(NULL
, domain
, error
);
669 * Trigger a no-op NETLOGON call. Lightweight version of
670 * wbcCheckTrustCredentials
673 wbcErr
wbcCtxPingDc(struct wbcContext
*ctx
, const char *domain
,
674 struct wbcAuthErrorInfo
**error
)
676 return wbcCtxPingDc2(ctx
, domain
, error
, NULL
);
680 wbcErr
wbcPingDc(const char *domain
, struct wbcAuthErrorInfo
**error
)
682 return wbcPingDc2(domain
, error
, NULL
);
686 * Trigger a no-op NETLOGON call. Lightweight version of
687 * wbcCheckTrustCredentials, optionally return attempted DC
690 wbcErr
wbcCtxPingDc2(struct wbcContext
*ctx
, const char *domain
,
691 struct wbcAuthErrorInfo
**error
, char **dcname
)
693 struct winbindd_request request
;
694 struct winbindd_response response
;
695 wbcErr wbc_status
= WBC_ERR_UNKNOWN_FAILURE
;
697 ZERO_STRUCT(request
);
698 ZERO_STRUCT(response
);
701 strncpy(request
.domain_name
, domain
,
702 sizeof(request
.domain_name
)-1);
707 wbc_status
= wbcRequestResponse(ctx
, WINBINDD_PING_DC
,
711 if (dcname
&& response
.extra_data
.data
) {
714 len
= response
.length
- sizeof(struct winbindd_response
);
715 *dcname
= wbcAllocateMemory(1, len
, NULL
);
716 BAIL_ON_PTR_ERROR(*dcname
, wbc_status
);
718 strlcpy(*dcname
, response
.extra_data
.data
, len
);
721 if (response
.data
.auth
.nt_status
!= 0) {
723 wbc_status
= wbc_create_error_info(&response
,
725 BAIL_ON_WBC_ERROR(wbc_status
);
728 wbc_status
= WBC_ERR_AUTH_ERROR
;
729 BAIL_ON_WBC_ERROR(wbc_status
);
731 BAIL_ON_WBC_ERROR(wbc_status
);
738 wbcErr
wbcPingDc2(const char *domain
, struct wbcAuthErrorInfo
**error
,
741 return wbcCtxPingDc2(NULL
, domain
, error
, dcname
);
744 /* Trigger an extended logoff notification to Winbind for a specific user */
746 wbcErr
wbcCtxLogoffUserEx(struct wbcContext
*ctx
,
747 const struct wbcLogoffUserParams
*params
,
748 struct wbcAuthErrorInfo
**error
)
750 struct winbindd_request request
;
751 struct winbindd_response response
;
752 wbcErr wbc_status
= WBC_ERR_UNKNOWN_FAILURE
;
757 if (!params
|| !params
->username
) {
758 wbc_status
= WBC_ERR_INVALID_PARAM
;
759 BAIL_ON_WBC_ERROR(wbc_status
);
762 if ((params
->num_blobs
> 0) && (params
->blobs
== NULL
)) {
763 wbc_status
= WBC_ERR_INVALID_PARAM
;
764 BAIL_ON_WBC_ERROR(wbc_status
);
766 if ((params
->num_blobs
== 0) && (params
->blobs
!= NULL
)) {
767 wbc_status
= WBC_ERR_INVALID_PARAM
;
768 BAIL_ON_WBC_ERROR(wbc_status
);
771 ZERO_STRUCT(request
);
772 ZERO_STRUCT(response
);
774 strncpy(request
.data
.logoff
.user
, params
->username
,
775 sizeof(request
.data
.logoff
.user
)-1);
777 for (i
=0; i
<params
->num_blobs
; i
++) {
779 if (strcasecmp(params
->blobs
[i
].name
, "ccfilename") == 0) {
780 if (params
->blobs
[i
].blob
.data
) {
781 strncpy(request
.data
.logoff
.krb5ccname
,
782 (const char *)params
->blobs
[i
].blob
.data
,
783 sizeof(request
.data
.logoff
.krb5ccname
) - 1);
788 if (strcasecmp(params
->blobs
[i
].name
, "user_uid") == 0) {
789 if (params
->blobs
[i
].blob
.data
) {
790 memcpy(&request
.data
.logoff
.uid
,
791 params
->blobs
[i
].blob
.data
,
792 MIN(params
->blobs
[i
].blob
.length
,
793 sizeof(request
.data
.logoff
.uid
)));
798 if (strcasecmp(params
->blobs
[i
].name
, "flags") == 0) {
799 if (params
->blobs
[i
].blob
.data
) {
800 memcpy(&request
.flags
,
801 params
->blobs
[i
].blob
.data
,
802 MIN(params
->blobs
[i
].blob
.length
,
803 sizeof(request
.flags
)));
811 wbc_status
= wbcRequestResponse(ctx
, WINBINDD_PAM_LOGOFF
,
815 /* Take the response above and return it to the caller */
816 if (response
.data
.auth
.nt_status
!= 0) {
818 wbc_status
= wbc_create_error_info(&response
,
820 BAIL_ON_WBC_ERROR(wbc_status
);
823 wbc_status
= WBC_ERR_AUTH_ERROR
;
824 BAIL_ON_WBC_ERROR(wbc_status
);
826 BAIL_ON_WBC_ERROR(wbc_status
);
833 wbcErr
wbcLogoffUserEx(const struct wbcLogoffUserParams
*params
,
834 struct wbcAuthErrorInfo
**error
)
836 return wbcCtxLogoffUserEx(NULL
, params
, error
);
839 /* Trigger a logoff notification to Winbind for a specific user */
841 wbcErr
wbcCtxLogoffUser(struct wbcContext
*ctx
,
842 const char *username
, uid_t uid
,
843 const char *ccfilename
)
845 struct winbindd_request request
;
846 struct winbindd_response response
;
847 wbcErr wbc_status
= WBC_ERR_UNKNOWN_FAILURE
;
852 wbc_status
= WBC_ERR_INVALID_PARAM
;
853 BAIL_ON_WBC_ERROR(wbc_status
);
856 ZERO_STRUCT(request
);
857 ZERO_STRUCT(response
);
859 strncpy(request
.data
.logoff
.user
, username
,
860 sizeof(request
.data
.logoff
.user
)-1);
861 request
.data
.logoff
.uid
= uid
;
864 strncpy(request
.data
.logoff
.krb5ccname
, ccfilename
,
865 sizeof(request
.data
.logoff
.krb5ccname
)-1);
870 wbc_status
= wbcRequestResponse(ctx
, WINBINDD_PAM_LOGOFF
,
874 /* Take the response above and return it to the caller */
881 wbcErr
wbcLogoffUser(const char *username
,
883 const char *ccfilename
)
885 return wbcCtxLogoffUser(NULL
, username
, uid
, ccfilename
);
888 /* Change a password for a user with more detailed information upon failure */
890 wbcErr
wbcCtxChangeUserPasswordEx(struct wbcContext
*ctx
,
891 const struct wbcChangePasswordParams
*params
,
892 struct wbcAuthErrorInfo
**error
,
893 enum wbcPasswordChangeRejectReason
*reject_reason
,
894 struct wbcUserPasswordPolicyInfo
**policy
)
896 struct winbindd_request request
;
897 struct winbindd_response response
;
898 wbcErr wbc_status
= WBC_ERR_UNKNOWN_FAILURE
;
903 if (!params
->account_name
) {
904 wbc_status
= WBC_ERR_INVALID_PARAM
;
920 ZERO_STRUCT(request
);
921 ZERO_STRUCT(response
);
923 switch (params
->level
) {
924 case WBC_CHANGE_PASSWORD_LEVEL_PLAIN
:
925 cmd
= WINBINDD_PAM_CHAUTHTOK
;
927 if (!params
->account_name
) {
928 wbc_status
= WBC_ERR_INVALID_PARAM
;
932 strncpy(request
.data
.chauthtok
.user
, params
->account_name
,
933 sizeof(request
.data
.chauthtok
.user
) - 1);
935 if (params
->old_password
.plaintext
) {
936 strncpy(request
.data
.chauthtok
.oldpass
,
937 params
->old_password
.plaintext
,
938 sizeof(request
.data
.chauthtok
.oldpass
) - 1);
941 if (params
->new_password
.plaintext
) {
942 strncpy(request
.data
.chauthtok
.newpass
,
943 params
->new_password
.plaintext
,
944 sizeof(request
.data
.chauthtok
.newpass
) - 1);
948 case WBC_CHANGE_PASSWORD_LEVEL_RESPONSE
:
949 cmd
= WINBINDD_PAM_CHNG_PSWD_AUTH_CRAP
;
951 if (!params
->account_name
|| !params
->domain_name
) {
952 wbc_status
= WBC_ERR_INVALID_PARAM
;
956 if (params
->old_password
.response
.old_lm_hash_enc_length
&&
957 !params
->old_password
.response
.old_lm_hash_enc_data
) {
958 wbc_status
= WBC_ERR_INVALID_PARAM
;
962 if (params
->old_password
.response
.old_lm_hash_enc_length
== 0 &&
963 params
->old_password
.response
.old_lm_hash_enc_data
) {
964 wbc_status
= WBC_ERR_INVALID_PARAM
;
968 if (params
->old_password
.response
.old_nt_hash_enc_length
&&
969 !params
->old_password
.response
.old_nt_hash_enc_data
) {
970 wbc_status
= WBC_ERR_INVALID_PARAM
;
974 if (params
->old_password
.response
.old_nt_hash_enc_length
== 0 &&
975 params
->old_password
.response
.old_nt_hash_enc_data
) {
976 wbc_status
= WBC_ERR_INVALID_PARAM
;
980 if (params
->new_password
.response
.lm_length
&&
981 !params
->new_password
.response
.lm_data
) {
982 wbc_status
= WBC_ERR_INVALID_PARAM
;
986 if (params
->new_password
.response
.lm_length
== 0 &&
987 params
->new_password
.response
.lm_data
) {
988 wbc_status
= WBC_ERR_INVALID_PARAM
;
992 if (params
->new_password
.response
.nt_length
&&
993 !params
->new_password
.response
.nt_data
) {
994 wbc_status
= WBC_ERR_INVALID_PARAM
;
998 if (params
->new_password
.response
.nt_length
== 0 &&
999 params
->new_password
.response
.nt_data
) {
1000 wbc_status
= WBC_ERR_INVALID_PARAM
;
1004 strncpy(request
.data
.chng_pswd_auth_crap
.user
,
1005 params
->account_name
,
1006 sizeof(request
.data
.chng_pswd_auth_crap
.user
) - 1);
1008 strncpy(request
.data
.chng_pswd_auth_crap
.domain
,
1009 params
->domain_name
,
1010 sizeof(request
.data
.chng_pswd_auth_crap
.domain
) - 1);
1012 if (params
->new_password
.response
.nt_data
) {
1013 request
.data
.chng_pswd_auth_crap
.new_nt_pswd_len
=
1014 params
->new_password
.response
.nt_length
;
1015 memcpy(request
.data
.chng_pswd_auth_crap
.new_nt_pswd
,
1016 params
->new_password
.response
.nt_data
,
1017 request
.data
.chng_pswd_auth_crap
.new_nt_pswd_len
);
1020 if (params
->new_password
.response
.lm_data
) {
1021 request
.data
.chng_pswd_auth_crap
.new_lm_pswd_len
=
1022 params
->new_password
.response
.lm_length
;
1023 memcpy(request
.data
.chng_pswd_auth_crap
.new_lm_pswd
,
1024 params
->new_password
.response
.lm_data
,
1025 request
.data
.chng_pswd_auth_crap
.new_lm_pswd_len
);
1028 if (params
->old_password
.response
.old_nt_hash_enc_data
) {
1029 request
.data
.chng_pswd_auth_crap
.old_nt_hash_enc_len
=
1030 params
->old_password
.response
.old_nt_hash_enc_length
;
1031 memcpy(request
.data
.chng_pswd_auth_crap
.old_nt_hash_enc
,
1032 params
->old_password
.response
.old_nt_hash_enc_data
,
1033 request
.data
.chng_pswd_auth_crap
.old_nt_hash_enc_len
);
1036 if (params
->old_password
.response
.old_lm_hash_enc_data
) {
1037 request
.data
.chng_pswd_auth_crap
.old_lm_hash_enc_len
=
1038 params
->old_password
.response
.old_lm_hash_enc_length
;
1039 memcpy(request
.data
.chng_pswd_auth_crap
.old_lm_hash_enc
,
1040 params
->old_password
.response
.old_lm_hash_enc_data
,
1041 request
.data
.chng_pswd_auth_crap
.old_lm_hash_enc_len
);
1046 wbc_status
= WBC_ERR_INVALID_PARAM
;
1053 wbc_status
= wbcRequestResponse(ctx
, cmd
,
1056 if (WBC_ERROR_IS_OK(wbc_status
)) {
1060 /* Take the response above and return it to the caller */
1062 if (response
.data
.auth
.nt_status
!= 0) {
1064 wbc_status
= wbc_create_error_info(&response
,
1066 BAIL_ON_WBC_ERROR(wbc_status
);
1072 wbc_status
= wbc_create_password_policy_info(&response
,
1074 BAIL_ON_WBC_ERROR(wbc_status
);
1077 if (reject_reason
) {
1078 *reject_reason
= response
.data
.auth
.reject_reason
;
1081 wbc_status
= WBC_ERR_PWD_CHANGE_FAILED
;
1082 BAIL_ON_WBC_ERROR(wbc_status
);
1089 wbcErr
wbcChangeUserPasswordEx(const struct wbcChangePasswordParams
*params
,
1090 struct wbcAuthErrorInfo
**error
,
1091 enum wbcPasswordChangeRejectReason
*reject_reason
,
1092 struct wbcUserPasswordPolicyInfo
**policy
)
1094 return wbcCtxChangeUserPasswordEx(NULL
, params
, error
,
1095 reject_reason
, policy
);
1098 /* Change a password for a user */
1100 wbcErr
wbcCtxChangeUserPassword(struct wbcContext
*ctx
,
1101 const char *username
,
1102 const char *old_password
,
1103 const char *new_password
)
1105 wbcErr wbc_status
= WBC_ERR_SUCCESS
;
1106 struct wbcChangePasswordParams params
;
1108 ZERO_STRUCT(params
);
1110 params
.account_name
= username
;
1111 params
.level
= WBC_CHANGE_PASSWORD_LEVEL_PLAIN
;
1112 params
.old_password
.plaintext
= old_password
;
1113 params
.new_password
.plaintext
= new_password
;
1115 wbc_status
= wbcCtxChangeUserPasswordEx(ctx
, ¶ms
,
1119 BAIL_ON_WBC_ERROR(wbc_status
);
1126 wbcErr
wbcChangeUserPassword(const char *username
,
1127 const char *old_password
,
1128 const char *new_password
)
1130 return wbcCtxChangeUserPassword(NULL
, username
,
1131 old_password
, new_password
);
1136 wbcErr
wbcCtxLogonUser(struct wbcContext
*ctx
,
1137 const struct wbcLogonUserParams
*params
,
1138 struct wbcLogonUserInfo
**info
,
1139 struct wbcAuthErrorInfo
**error
,
1140 struct wbcUserPasswordPolicyInfo
**policy
)
1142 wbcErr wbc_status
= WBC_ERR_UNKNOWN_FAILURE
;
1143 struct winbindd_request request
;
1144 struct winbindd_response response
;
1147 ZERO_STRUCT(request
);
1148 ZERO_STRUCT(response
);
1161 wbc_status
= WBC_ERR_INVALID_PARAM
;
1162 BAIL_ON_WBC_ERROR(wbc_status
);
1165 if (!params
->username
) {
1166 wbc_status
= WBC_ERR_INVALID_PARAM
;
1167 BAIL_ON_WBC_ERROR(wbc_status
);
1170 if ((params
->num_blobs
> 0) && (params
->blobs
== NULL
)) {
1171 wbc_status
= WBC_ERR_INVALID_PARAM
;
1172 BAIL_ON_WBC_ERROR(wbc_status
);
1174 if ((params
->num_blobs
== 0) && (params
->blobs
!= NULL
)) {
1175 wbc_status
= WBC_ERR_INVALID_PARAM
;
1176 BAIL_ON_WBC_ERROR(wbc_status
);
1179 /* Initialize request */
1181 request
.flags
= WBFLAG_PAM_INFO3_TEXT
|
1182 WBFLAG_PAM_USER_SESSION_KEY
|
1185 if (!params
->password
) {
1186 wbc_status
= WBC_ERR_INVALID_PARAM
;
1187 BAIL_ON_WBC_ERROR(wbc_status
);
1190 strncpy(request
.data
.auth
.user
,
1192 sizeof(request
.data
.auth
.user
)-1);
1194 strncpy(request
.data
.auth
.pass
,
1196 sizeof(request
.data
.auth
.pass
)-1);
1198 for (i
=0; i
<params
->num_blobs
; i
++) {
1200 if (strcasecmp(params
->blobs
[i
].name
, "krb5_cc_type") == 0) {
1201 if (params
->blobs
[i
].blob
.data
) {
1202 strncpy(request
.data
.auth
.krb5_cc_type
,
1203 (const char *)params
->blobs
[i
].blob
.data
,
1204 sizeof(request
.data
.auth
.krb5_cc_type
) - 1);
1209 if (strcasecmp(params
->blobs
[i
].name
, "user_uid") == 0) {
1210 if (params
->blobs
[i
].blob
.data
) {
1211 memcpy(&request
.data
.auth
.uid
,
1212 params
->blobs
[i
].blob
.data
,
1213 MIN(sizeof(request
.data
.auth
.uid
),
1214 params
->blobs
[i
].blob
.length
));
1219 if (strcasecmp(params
->blobs
[i
].name
, "flags") == 0) {
1220 if (params
->blobs
[i
].blob
.data
) {
1223 params
->blobs
[i
].blob
.data
,
1225 params
->blobs
[i
].blob
.length
));
1226 request
.flags
|= flags
;
1231 if (strcasecmp(params
->blobs
[i
].name
, "membership_of") == 0) {
1232 if (params
->blobs
[i
].blob
.data
&&
1233 params
->blobs
[i
].blob
.data
[0] > 0) {
1234 strncpy(request
.data
.auth
.require_membership_of_sid
,
1235 (const char *)params
->blobs
[i
].blob
.data
,
1236 sizeof(request
.data
.auth
.require_membership_of_sid
) - 1);
1242 wbc_status
= wbcRequestResponse(ctx
, WINBINDD_PAM_AUTH
,
1246 if (response
.data
.auth
.nt_status
!= 0) {
1248 wbc_status
= wbc_create_error_info(&response
,
1250 BAIL_ON_WBC_ERROR(wbc_status
);
1253 wbc_status
= WBC_ERR_AUTH_ERROR
;
1254 BAIL_ON_WBC_ERROR(wbc_status
);
1256 BAIL_ON_WBC_ERROR(wbc_status
);
1259 wbc_status
= wbc_create_logon_info(&response
,
1261 BAIL_ON_WBC_ERROR(wbc_status
);
1265 wbc_status
= wbc_create_password_policy_info(&response
,
1267 BAIL_ON_WBC_ERROR(wbc_status
);
1271 winbindd_free_response(&response
);
1277 wbcErr
wbcLogonUser(const struct wbcLogonUserParams
*params
,
1278 struct wbcLogonUserInfo
**info
,
1279 struct wbcAuthErrorInfo
**error
,
1280 struct wbcUserPasswordPolicyInfo
**policy
)
1282 return wbcCtxLogonUser(NULL
, params
, info
, error
, policy
);
1285 static void wbcCredentialCacheInfoDestructor(void *ptr
)
1287 struct wbcCredentialCacheInfo
*i
=
1288 (struct wbcCredentialCacheInfo
*)ptr
;
1289 wbcFreeMemory(i
->blobs
);
1292 /* Authenticate a user with cached credentials */
1294 wbcErr
wbcCtxCredentialCache(struct wbcContext
*ctx
,
1295 struct wbcCredentialCacheParams
*params
,
1296 struct wbcCredentialCacheInfo
**info
,
1297 struct wbcAuthErrorInfo
**error
)
1299 wbcErr status
= WBC_ERR_UNKNOWN_FAILURE
;
1300 struct wbcCredentialCacheInfo
*result
= NULL
;
1301 struct winbindd_request request
;
1302 struct winbindd_response response
;
1303 struct wbcNamedBlob
*initial_blob
= NULL
;
1304 struct wbcNamedBlob
*challenge_blob
= NULL
;
1307 ZERO_STRUCT(request
);
1308 ZERO_STRUCT(response
);
1312 if (error
!= NULL
) {
1315 if ((params
== NULL
)
1316 || (params
->account_name
== NULL
)
1317 || (params
->level
!= WBC_CREDENTIAL_CACHE_LEVEL_NTLMSSP
)) {
1318 status
= WBC_ERR_INVALID_PARAM
;
1322 for (i
=0; i
<params
->num_blobs
; i
++) {
1324 * Older callers may used to provide the NEGOTIATE request
1325 * as "initial_blob", but it was completely ignored by winbindd.
1327 * So we keep ignoring it.
1329 * A new callers that is capable to support "new_spnego",
1330 * will provide the NEGOTIATE request as "negotiate_blob"
1333 if (strcasecmp(params
->blobs
[i
].name
, "negotiate_blob") == 0) {
1334 if (initial_blob
!= NULL
) {
1335 status
= WBC_ERR_INVALID_PARAM
;
1338 initial_blob
= ¶ms
->blobs
[i
];
1341 if (strcasecmp(params
->blobs
[i
].name
, "challenge_blob") == 0) {
1342 if (challenge_blob
!= NULL
) {
1343 status
= WBC_ERR_INVALID_PARAM
;
1346 challenge_blob
= ¶ms
->blobs
[i
];
1351 if (params
->domain_name
!= NULL
) {
1352 status
= wbcRequestResponse(ctx
, WINBINDD_INFO
,
1354 if (!WBC_ERROR_IS_OK(status
)) {
1357 snprintf(request
.data
.ccache_ntlm_auth
.user
,
1358 sizeof(request
.data
.ccache_ntlm_auth
.user
)-1,
1359 "%s%c%s", params
->domain_name
,
1360 response
.data
.info
.winbind_separator
,
1361 params
->account_name
);
1363 strncpy(request
.data
.ccache_ntlm_auth
.user
,
1364 params
->account_name
,
1365 sizeof(request
.data
.ccache_ntlm_auth
.user
)-1);
1367 request
.data
.ccache_ntlm_auth
.uid
= getuid();
1369 request
.data
.ccache_ntlm_auth
.initial_blob_len
= 0;
1370 request
.data
.ccache_ntlm_auth
.challenge_blob_len
= 0;
1371 request
.extra_len
= 0;
1373 if (initial_blob
!= NULL
) {
1374 request
.data
.ccache_ntlm_auth
.initial_blob_len
=
1375 initial_blob
->blob
.length
;
1376 request
.extra_len
+= initial_blob
->blob
.length
;
1378 if (challenge_blob
!= NULL
) {
1379 request
.data
.ccache_ntlm_auth
.challenge_blob_len
=
1380 challenge_blob
->blob
.length
;
1381 request
.extra_len
+= challenge_blob
->blob
.length
;
1384 if (request
.extra_len
!= 0) {
1385 request
.extra_data
.data
= (char *)malloc(request
.extra_len
);
1386 if (request
.extra_data
.data
== NULL
) {
1387 status
= WBC_ERR_NO_MEMORY
;
1391 if (initial_blob
!= NULL
) {
1392 memcpy(request
.extra_data
.data
,
1393 initial_blob
->blob
.data
, initial_blob
->blob
.length
);
1395 if (challenge_blob
!= NULL
) {
1396 memcpy(request
.extra_data
.data
1397 + request
.data
.ccache_ntlm_auth
.initial_blob_len
,
1398 challenge_blob
->blob
.data
,
1399 challenge_blob
->blob
.length
);
1402 status
= wbcRequestResponse(ctx
, WINBINDD_CCACHE_NTLMAUTH
,
1403 &request
, &response
);
1404 if (!WBC_ERROR_IS_OK(status
)) {
1408 result
= (struct wbcCredentialCacheInfo
*)wbcAllocateMemory(
1409 1, sizeof(struct wbcCredentialCacheInfo
),
1410 wbcCredentialCacheInfoDestructor
);
1411 if (result
== NULL
) {
1412 status
= WBC_ERR_NO_MEMORY
;
1415 result
->num_blobs
= 0;
1416 result
->blobs
= NULL
;
1417 status
= wbcAddNamedBlob(&result
->num_blobs
, &result
->blobs
,
1419 (uint8_t *)response
.extra_data
.data
,
1420 response
.data
.ccache_ntlm_auth
.auth_blob_len
);
1421 if (!WBC_ERROR_IS_OK(status
)) {
1424 status
= wbcAddNamedBlob(
1425 &result
->num_blobs
, &result
->blobs
, "session_key", 0,
1426 response
.data
.ccache_ntlm_auth
.session_key
,
1427 sizeof(response
.data
.ccache_ntlm_auth
.session_key
));
1428 if (!WBC_ERROR_IS_OK(status
)) {
1431 if (response
.data
.ccache_ntlm_auth
.new_spnego
) {
1432 status
= wbcAddNamedBlob(
1433 &result
->num_blobs
, &result
->blobs
, "new_spnego", 0,
1434 &response
.data
.ccache_ntlm_auth
.new_spnego
,
1435 sizeof(response
.data
.ccache_ntlm_auth
.new_spnego
));
1436 if (!WBC_ERROR_IS_OK(status
)) {
1443 status
= WBC_ERR_SUCCESS
;
1445 free(request
.extra_data
.data
);
1446 winbindd_free_response(&response
);
1447 wbcFreeMemory(result
);
1452 wbcErr
wbcCredentialCache(struct wbcCredentialCacheParams
*params
,
1453 struct wbcCredentialCacheInfo
**info
,
1454 struct wbcAuthErrorInfo
**error
)
1456 return wbcCtxCredentialCache(NULL
, params
, info
, error
);
1459 /* Authenticate a user with cached credentials */
1461 wbcErr
wbcCtxCredentialSave(struct wbcContext
*ctx
,
1462 const char *user
, const char *password
)
1464 struct winbindd_request request
;
1465 struct winbindd_response response
;
1467 ZERO_STRUCT(request
);
1468 ZERO_STRUCT(response
);
1470 strncpy(request
.data
.ccache_save
.user
, user
,
1471 sizeof(request
.data
.ccache_save
.user
)-1);
1472 strncpy(request
.data
.ccache_save
.pass
, password
,
1473 sizeof(request
.data
.ccache_save
.pass
)-1);
1474 request
.data
.ccache_save
.uid
= getuid();
1476 return wbcRequestResponse(ctx
, WINBINDD_CCACHE_SAVE
, &request
, &response
);
1480 wbcErr
wbcCredentialSave(const char *user
, const char *password
)
1482 return wbcCtxCredentialSave(NULL
, user
, password
);