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