libwbclient: wbc_create_logon_info is always called with mem_ctx==NULL
[nivanova/samba-autobuild/.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(const struct winbindd_response *resp,
211                                     struct wbcAuthErrorInfo **_e)
212 {
213         wbcErr wbc_status = WBC_ERR_SUCCESS;
214         struct wbcAuthErrorInfo *e;
215
216         e = talloc(NULL, struct wbcAuthErrorInfo);
217         BAIL_ON_PTR_ERROR(e, wbc_status);
218
219         e->nt_status = resp->data.auth.nt_status;
220         e->pam_error = resp->data.auth.pam_error;
221         e->nt_string = talloc_strdup(e, resp->data.auth.nt_status_string);
222         BAIL_ON_PTR_ERROR(e->nt_string, wbc_status);
223
224         e->display_string = talloc_strdup(e, resp->data.auth.error_string);
225         BAIL_ON_PTR_ERROR(e->display_string, wbc_status);
226
227         *_e = e;
228         e = NULL;
229
230 done:
231         talloc_free(e);
232         return wbc_status;
233 }
234
235 static wbcErr wbc_create_password_policy_info(const struct winbindd_response *resp,
236                                               struct wbcUserPasswordPolicyInfo **_i)
237 {
238         wbcErr wbc_status = WBC_ERR_SUCCESS;
239         struct wbcUserPasswordPolicyInfo *i;
240
241         i = talloc(NULL, struct wbcUserPasswordPolicyInfo);
242         BAIL_ON_PTR_ERROR(i, wbc_status);
243
244         i->min_passwordage      = resp->data.auth.policy.min_passwordage;
245         i->min_length_password  = resp->data.auth.policy.min_length_password;
246         i->password_history     = resp->data.auth.policy.password_history;
247         i->password_properties  = resp->data.auth.policy.password_properties;
248         i->expire               = resp->data.auth.policy.expire;
249
250         *_i = i;
251         i = NULL;
252
253 done:
254         talloc_free(i);
255         return wbc_status;
256 }
257
258 static wbcErr wbc_create_logon_info(struct winbindd_response *resp,
259                                     struct wbcLogonUserInfo **_i)
260 {
261         wbcErr wbc_status = WBC_ERR_SUCCESS;
262         struct wbcLogonUserInfo *i;
263
264         i = talloc_zero(NULL, struct wbcLogonUserInfo);
265         BAIL_ON_PTR_ERROR(i, wbc_status);
266
267         wbc_status = wbc_create_auth_info(i, resp, &i->info);
268         BAIL_ON_WBC_ERROR(wbc_status);
269
270         if (resp->data.auth.krb5ccname &&
271             strlen(resp->data.auth.krb5ccname)) {
272                 wbc_status = wbcAddNamedBlob(&i->num_blobs,
273                                              &i->blobs,
274                                              "krb5ccname",
275                                              0,
276                                              (uint8_t *)resp->data.auth.krb5ccname,
277                                              strlen(resp->data.auth.krb5ccname)+1);
278                 BAIL_ON_WBC_ERROR(wbc_status);
279         }
280
281         if (resp->data.auth.unix_username &&
282             strlen(resp->data.auth.unix_username)) {
283                 wbc_status = wbcAddNamedBlob(&i->num_blobs,
284                                              &i->blobs,
285                                              "unix_username",
286                                              0,
287                                              (uint8_t *)resp->data.auth.unix_username,
288                                              strlen(resp->data.auth.unix_username)+1);
289                 BAIL_ON_WBC_ERROR(wbc_status);
290         }
291
292         *_i = i;
293         i = NULL;
294 done:
295         if (!WBC_ERROR_IS_OK(wbc_status) && i) {
296                 wbcFreeMemory(i->blobs);
297         }
298
299         talloc_free(i);
300         return wbc_status;
301 }
302
303 /* Authenticate with more detailed information */
304 wbcErr wbcAuthenticateUserEx(const struct wbcAuthUserParams *params,
305                              struct wbcAuthUserInfo **info,
306                              struct wbcAuthErrorInfo **error)
307 {
308         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
309         int cmd = 0;
310         struct winbindd_request request;
311         struct winbindd_response response;
312
313         ZERO_STRUCT(request);
314         ZERO_STRUCT(response);
315
316         if (error) {
317                 *error = NULL;
318         }
319
320         if (!params) {
321                 wbc_status = WBC_ERR_INVALID_PARAM;
322                 BAIL_ON_WBC_ERROR(wbc_status);
323         }
324
325         if (!params->account_name) {
326                 wbc_status = WBC_ERR_INVALID_PARAM;
327                 BAIL_ON_WBC_ERROR(wbc_status);
328         }
329
330         /* Initialize request */
331
332         switch (params->level) {
333         case WBC_AUTH_USER_LEVEL_PLAIN:
334                 cmd = WINBINDD_PAM_AUTH;
335                 request.flags = WBFLAG_PAM_INFO3_TEXT |
336                                 WBFLAG_PAM_USER_SESSION_KEY |
337                                 WBFLAG_PAM_LMKEY;
338
339                 if (!params->password.plaintext) {
340                         wbc_status = WBC_ERR_INVALID_PARAM;
341                         BAIL_ON_WBC_ERROR(wbc_status);
342                 }
343
344                 if (params->domain_name && params->domain_name[0]) {
345                         /* We need to get the winbind separator :-( */
346                         struct winbindd_response sep_response;
347
348                         ZERO_STRUCT(sep_response);
349
350                         wbc_status = wbcRequestResponse(WINBINDD_INFO,
351                                                         NULL, &sep_response);
352                         BAIL_ON_WBC_ERROR(wbc_status);
353
354                         snprintf(request.data.auth.user,
355                                  sizeof(request.data.auth.user)-1,
356                                  "%s%c%s",
357                                  params->domain_name,
358                                  sep_response.data.info.winbind_separator,
359                                  params->account_name);
360                 } else {
361                         strncpy(request.data.auth.user,
362                                 params->account_name,
363                                 sizeof(request.data.auth.user)-1);
364                 }
365
366                 strncpy(request.data.auth.pass,
367                         params->password.plaintext,
368                         sizeof(request.data.auth.pass)-1);
369                 break;
370
371         case WBC_AUTH_USER_LEVEL_HASH:
372                 wbc_status = WBC_ERR_NOT_IMPLEMENTED;
373                 BAIL_ON_WBC_ERROR(wbc_status);
374                 break;
375
376         case WBC_AUTH_USER_LEVEL_RESPONSE:
377                 cmd = WINBINDD_PAM_AUTH_CRAP;
378                 request.flags = WBFLAG_PAM_INFO3_TEXT |
379                                 WBFLAG_PAM_USER_SESSION_KEY |
380                                 WBFLAG_PAM_LMKEY;
381
382                 if (params->password.response.lm_length &&
383                     !params->password.response.lm_data) {
384                         wbc_status = WBC_ERR_INVALID_PARAM;
385                         BAIL_ON_WBC_ERROR(wbc_status);
386                 }
387                 if (params->password.response.lm_length == 0 &&
388                     params->password.response.lm_data) {
389                         wbc_status = WBC_ERR_INVALID_PARAM;
390                         BAIL_ON_WBC_ERROR(wbc_status);
391                 }
392
393                 if (params->password.response.nt_length &&
394                     !params->password.response.nt_data) {
395                         wbc_status = WBC_ERR_INVALID_PARAM;
396                         BAIL_ON_WBC_ERROR(wbc_status);
397                 }
398                 if (params->password.response.nt_length == 0&&
399                     params->password.response.nt_data) {
400                         wbc_status = WBC_ERR_INVALID_PARAM;
401                         BAIL_ON_WBC_ERROR(wbc_status);
402                 }
403
404                 strncpy(request.data.auth_crap.user,
405                         params->account_name,
406                         sizeof(request.data.auth_crap.user)-1);
407                 if (params->domain_name) {
408                         strncpy(request.data.auth_crap.domain,
409                                 params->domain_name,
410                                 sizeof(request.data.auth_crap.domain)-1);
411                 }
412                 if (params->workstation_name) {
413                         strncpy(request.data.auth_crap.workstation,
414                                 params->workstation_name,
415                                 sizeof(request.data.auth_crap.workstation)-1);
416                 }
417
418                 request.data.auth_crap.logon_parameters =
419                                 params->parameter_control;
420
421                 memcpy(request.data.auth_crap.chal,
422                        params->password.response.challenge,
423                        sizeof(request.data.auth_crap.chal));
424
425                 request.data.auth_crap.lm_resp_len =
426                                 MIN(params->password.response.lm_length,
427                                     sizeof(request.data.auth_crap.lm_resp));
428                 if (params->password.response.lm_data) {
429                         memcpy(request.data.auth_crap.lm_resp,
430                                params->password.response.lm_data,
431                                request.data.auth_crap.lm_resp_len);
432                 }
433                 request.data.auth_crap.nt_resp_len = params->password.response.nt_length;
434                 if (params->password.response.nt_length > sizeof(request.data.auth_crap.nt_resp)) {
435                         request.flags |= WBFLAG_BIG_NTLMV2_BLOB;
436                         request.extra_len = params->password.response.nt_length;
437                         request.extra_data.data = talloc_zero_array(NULL, char, request.extra_len);
438                         if (request.extra_data.data == NULL) {
439                                 wbc_status = WBC_ERR_NO_MEMORY;
440                                 BAIL_ON_WBC_ERROR(wbc_status);
441                         }
442                         memcpy(request.extra_data.data,
443                                params->password.response.nt_data,
444                                request.data.auth_crap.nt_resp_len);
445                 } else if (params->password.response.nt_data) {
446                         memcpy(request.data.auth_crap.nt_resp,
447                                params->password.response.nt_data,
448                                request.data.auth_crap.nt_resp_len);
449                 }
450                 break;
451         default:
452                 break;
453         }
454
455         if (cmd == 0) {
456                 wbc_status = WBC_ERR_INVALID_PARAM;
457                 BAIL_ON_WBC_ERROR(wbc_status);
458         }
459
460         if (params->flags) {
461                 request.flags |= params->flags;
462         }
463
464         wbc_status = wbcRequestResponse(cmd,
465                                         &request,
466                                         &response);
467         if (response.data.auth.nt_status != 0) {
468                 if (error) {
469                         wbc_status = wbc_create_error_info(&response,
470                                                            error);
471                         BAIL_ON_WBC_ERROR(wbc_status);
472                 }
473
474                 wbc_status = WBC_ERR_AUTH_ERROR;
475                 BAIL_ON_WBC_ERROR(wbc_status);
476         }
477         BAIL_ON_WBC_ERROR(wbc_status);
478
479         if (info) {
480                 wbc_status = wbc_create_auth_info(NULL,
481                                                   &response,
482                                                   info);
483                 BAIL_ON_WBC_ERROR(wbc_status);
484         }
485
486 done:
487         winbindd_free_response(&response);
488
489         talloc_free(request.extra_data.data);
490
491         return wbc_status;
492 }
493
494 /* Trigger a verification of the trust credentials of a specific domain */
495 wbcErr wbcCheckTrustCredentials(const char *domain,
496                                 struct wbcAuthErrorInfo **error)
497 {
498         struct winbindd_request request;
499         struct winbindd_response response;
500         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
501
502         ZERO_STRUCT(request);
503         ZERO_STRUCT(response);
504
505         if (domain) {
506                 strncpy(request.domain_name, domain,
507                         sizeof(request.domain_name)-1);
508         }
509
510         /* Send request */
511
512         wbc_status = wbcRequestResponse(WINBINDD_CHECK_MACHACC,
513                                         &request,
514                                         &response);
515         if (response.data.auth.nt_status != 0) {
516                 if (error) {
517                         wbc_status = wbc_create_error_info(&response,
518                                                            error);
519                         BAIL_ON_WBC_ERROR(wbc_status);
520                 }
521
522                 wbc_status = WBC_ERR_AUTH_ERROR;
523                 BAIL_ON_WBC_ERROR(wbc_status);
524         }
525         BAIL_ON_WBC_ERROR(wbc_status);
526
527  done:
528         return wbc_status;
529 }
530
531 /* Trigger a change of the trust credentials for a specific domain */
532 wbcErr wbcChangeTrustCredentials(const char *domain,
533                                  struct wbcAuthErrorInfo **error)
534 {
535         struct winbindd_request request;
536         struct winbindd_response response;
537         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
538
539         ZERO_STRUCT(request);
540         ZERO_STRUCT(response);
541
542         if (domain) {
543                 strncpy(request.domain_name, domain,
544                         sizeof(request.domain_name)-1);
545         }
546
547         /* Send request */
548
549         wbc_status = wbcRequestResponse(WINBINDD_CHANGE_MACHACC,
550                                         &request,
551                                         &response);
552         if (response.data.auth.nt_status != 0) {
553                 if (error) {
554                         wbc_status = wbc_create_error_info(&response,
555                                                            error);
556                         BAIL_ON_WBC_ERROR(wbc_status);
557                 }
558
559                 wbc_status = WBC_ERR_AUTH_ERROR;
560                 BAIL_ON_WBC_ERROR(wbc_status);
561         }
562         BAIL_ON_WBC_ERROR(wbc_status);
563
564  done:
565         return wbc_status;
566 }
567
568 /*
569  * Trigger a no-op NETLOGON call. Lightweight version of
570  * wbcCheckTrustCredentials
571  */
572 wbcErr wbcPingDc(const char *domain, struct wbcAuthErrorInfo **error)
573 {
574         struct winbindd_request request;
575         struct winbindd_response response;
576         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
577
578         if (domain) {
579                 /*
580                  * the current protocol doesn't support
581                  * specifying a domain
582                  */
583                 wbc_status = WBC_ERR_NOT_IMPLEMENTED;
584                 BAIL_ON_WBC_ERROR(wbc_status);
585         }
586
587         ZERO_STRUCT(request);
588         ZERO_STRUCT(response);
589
590         /* Send request */
591
592         wbc_status = wbcRequestResponse(WINBINDD_PING_DC,
593                                         &request,
594                                         &response);
595         if (response.data.auth.nt_status != 0) {
596                 if (error) {
597                         wbc_status = wbc_create_error_info(&response,
598                                                            error);
599                         BAIL_ON_WBC_ERROR(wbc_status);
600                 }
601
602                 wbc_status = WBC_ERR_AUTH_ERROR;
603                 BAIL_ON_WBC_ERROR(wbc_status);
604         }
605         BAIL_ON_WBC_ERROR(wbc_status);
606
607  done:
608         return wbc_status;
609 }
610
611 /* Trigger an extended logoff notification to Winbind for a specific user */
612 wbcErr wbcLogoffUserEx(const struct wbcLogoffUserParams *params,
613                        struct wbcAuthErrorInfo **error)
614 {
615         struct winbindd_request request;
616         struct winbindd_response response;
617         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
618         int i;
619
620         /* validate input */
621
622         if (!params || !params->username) {
623                 wbc_status = WBC_ERR_INVALID_PARAM;
624                 BAIL_ON_WBC_ERROR(wbc_status);
625         }
626
627         if ((params->num_blobs > 0) && (params->blobs == NULL)) {
628                 wbc_status = WBC_ERR_INVALID_PARAM;
629                 BAIL_ON_WBC_ERROR(wbc_status);
630         }
631         if ((params->num_blobs == 0) && (params->blobs != NULL)) {
632                 wbc_status = WBC_ERR_INVALID_PARAM;
633                 BAIL_ON_WBC_ERROR(wbc_status);
634         }
635
636         ZERO_STRUCT(request);
637         ZERO_STRUCT(response);
638
639         strncpy(request.data.logoff.user, params->username,
640                 sizeof(request.data.logoff.user)-1);
641
642         for (i=0; i<params->num_blobs; i++) {
643
644                 if (strcasecmp(params->blobs[i].name, "ccfilename") == 0) {
645                         if (params->blobs[i].blob.data) {
646                                 strncpy(request.data.logoff.krb5ccname,
647                                         (const char *)params->blobs[i].blob.data,
648                                         sizeof(request.data.logoff.krb5ccname) - 1);
649                         }
650                         continue;
651                 }
652
653                 if (strcasecmp(params->blobs[i].name, "user_uid") == 0) {
654                         if (params->blobs[i].blob.data) {
655                                 memcpy(&request.data.logoff.uid,
656                                         params->blobs[i].blob.data,
657                                         MIN(params->blobs[i].blob.length,
658                                             sizeof(request.data.logoff.uid)));
659                         }
660                         continue;
661                 }
662
663                 if (strcasecmp(params->blobs[i].name, "flags") == 0) {
664                         if (params->blobs[i].blob.data) {
665                                 memcpy(&request.flags,
666                                         params->blobs[i].blob.data,
667                                         MIN(params->blobs[i].blob.length,
668                                             sizeof(request.flags)));
669                         }
670                         continue;
671                 }
672         }
673
674         /* Send request */
675
676         wbc_status = wbcRequestResponse(WINBINDD_PAM_LOGOFF,
677                                         &request,
678                                         &response);
679
680         /* Take the response above and return it to the caller */
681         if (response.data.auth.nt_status != 0) {
682                 if (error) {
683                         wbc_status = wbc_create_error_info(&response,
684                                                            error);
685                         BAIL_ON_WBC_ERROR(wbc_status);
686                 }
687
688                 wbc_status = WBC_ERR_AUTH_ERROR;
689                 BAIL_ON_WBC_ERROR(wbc_status);
690         }
691         BAIL_ON_WBC_ERROR(wbc_status);
692
693  done:
694         return wbc_status;
695 }
696
697 /* Trigger a logoff notification to Winbind for a specific user */
698 wbcErr wbcLogoffUser(const char *username,
699                      uid_t uid,
700                      const char *ccfilename)
701 {
702         struct winbindd_request request;
703         struct winbindd_response response;
704         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
705
706         /* validate input */
707
708         if (!username) {
709                 wbc_status = WBC_ERR_INVALID_PARAM;
710                 BAIL_ON_WBC_ERROR(wbc_status);
711         }
712
713         ZERO_STRUCT(request);
714         ZERO_STRUCT(response);
715
716         strncpy(request.data.logoff.user, username,
717                 sizeof(request.data.logoff.user)-1);
718         request.data.logoff.uid = uid;
719
720         if (ccfilename) {
721                 strncpy(request.data.logoff.krb5ccname, ccfilename,
722                         sizeof(request.data.logoff.krb5ccname)-1);
723         }
724
725         /* Send request */
726
727         wbc_status = wbcRequestResponse(WINBINDD_PAM_LOGOFF,
728                                         &request,
729                                         &response);
730
731         /* Take the response above and return it to the caller */
732
733  done:
734         return wbc_status;
735 }
736
737 /* Change a password for a user with more detailed information upon failure */
738 wbcErr wbcChangeUserPasswordEx(const struct wbcChangePasswordParams *params,
739                                struct wbcAuthErrorInfo **error,
740                                enum wbcPasswordChangeRejectReason *reject_reason,
741                                struct wbcUserPasswordPolicyInfo **policy)
742 {
743         struct winbindd_request request;
744         struct winbindd_response response;
745         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
746         int cmd = 0;
747
748         /* validate input */
749
750         if (!params->account_name) {
751                 wbc_status = WBC_ERR_INVALID_PARAM;
752                 BAIL_ON_WBC_ERROR(wbc_status);
753         }
754
755         if (error) {
756                 *error = NULL;
757         }
758
759         if (policy) {
760                 *policy = NULL;
761         }
762
763         if (reject_reason) {
764                 *reject_reason = -1;
765         }
766
767         ZERO_STRUCT(request);
768         ZERO_STRUCT(response);
769
770         switch (params->level) {
771         case WBC_CHANGE_PASSWORD_LEVEL_PLAIN:
772                 cmd = WINBINDD_PAM_CHAUTHTOK;
773
774                 if (!params->account_name) {
775                         wbc_status = WBC_ERR_INVALID_PARAM;
776                         BAIL_ON_WBC_ERROR(wbc_status);
777                 }
778
779                 strncpy(request.data.chauthtok.user, params->account_name,
780                         sizeof(request.data.chauthtok.user) - 1);
781
782                 if (params->old_password.plaintext) {
783                         strncpy(request.data.chauthtok.oldpass,
784                                 params->old_password.plaintext,
785                                 sizeof(request.data.chauthtok.oldpass) - 1);
786                 }
787
788                 if (params->new_password.plaintext) {
789                         strncpy(request.data.chauthtok.newpass,
790                                 params->new_password.plaintext,
791                                 sizeof(request.data.chauthtok.newpass) - 1);
792                 }
793                 break;
794
795         case WBC_CHANGE_PASSWORD_LEVEL_RESPONSE:
796                 cmd = WINBINDD_PAM_CHNG_PSWD_AUTH_CRAP;
797
798                 if (!params->account_name || !params->domain_name) {
799                         wbc_status = WBC_ERR_INVALID_PARAM;
800                         BAIL_ON_WBC_ERROR(wbc_status);
801                 }
802
803                 if (params->old_password.response.old_lm_hash_enc_length &&
804                     !params->old_password.response.old_lm_hash_enc_data) {
805                         wbc_status = WBC_ERR_INVALID_PARAM;
806                         BAIL_ON_WBC_ERROR(wbc_status);
807                 }
808
809                 if (params->old_password.response.old_lm_hash_enc_length == 0 &&
810                     params->old_password.response.old_lm_hash_enc_data) {
811                         wbc_status = WBC_ERR_INVALID_PARAM;
812                         BAIL_ON_WBC_ERROR(wbc_status);
813                 }
814
815                 if (params->old_password.response.old_nt_hash_enc_length &&
816                     !params->old_password.response.old_nt_hash_enc_data) {
817                         wbc_status = WBC_ERR_INVALID_PARAM;
818                         BAIL_ON_WBC_ERROR(wbc_status);
819                 }
820
821                 if (params->old_password.response.old_nt_hash_enc_length == 0 &&
822                     params->old_password.response.old_nt_hash_enc_data) {
823                         wbc_status = WBC_ERR_INVALID_PARAM;
824                         BAIL_ON_WBC_ERROR(wbc_status);
825                 }
826
827                 if (params->new_password.response.lm_length &&
828                     !params->new_password.response.lm_data) {
829                         wbc_status = WBC_ERR_INVALID_PARAM;
830                         BAIL_ON_WBC_ERROR(wbc_status);
831                 }
832
833                 if (params->new_password.response.lm_length == 0 &&
834                     params->new_password.response.lm_data) {
835                         wbc_status = WBC_ERR_INVALID_PARAM;
836                         BAIL_ON_WBC_ERROR(wbc_status);
837                 }
838
839                 if (params->new_password.response.nt_length &&
840                     !params->new_password.response.nt_data) {
841                         wbc_status = WBC_ERR_INVALID_PARAM;
842                         BAIL_ON_WBC_ERROR(wbc_status);
843                 }
844
845                 if (params->new_password.response.nt_length == 0 &&
846                     params->new_password.response.nt_data) {
847                         wbc_status = WBC_ERR_INVALID_PARAM;
848                         BAIL_ON_WBC_ERROR(wbc_status);
849                 }
850
851                 strncpy(request.data.chng_pswd_auth_crap.user,
852                         params->account_name,
853                         sizeof(request.data.chng_pswd_auth_crap.user) - 1);
854
855                 strncpy(request.data.chng_pswd_auth_crap.domain,
856                         params->domain_name,
857                         sizeof(request.data.chng_pswd_auth_crap.domain) - 1);
858
859                 if (params->new_password.response.nt_data) {
860                         memcpy(request.data.chng_pswd_auth_crap.new_nt_pswd,
861                                params->new_password.response.nt_data,
862                                request.data.chng_pswd_auth_crap.new_nt_pswd_len);
863                         request.data.chng_pswd_auth_crap.new_nt_pswd_len =
864                                 params->new_password.response.nt_length;
865                 }
866
867                 if (params->new_password.response.lm_data) {
868                         memcpy(request.data.chng_pswd_auth_crap.new_lm_pswd,
869                                params->new_password.response.lm_data,
870                                request.data.chng_pswd_auth_crap.new_lm_pswd_len);
871                         request.data.chng_pswd_auth_crap.new_lm_pswd_len =
872                                 params->new_password.response.lm_length;
873                 }
874
875                 if (params->old_password.response.old_nt_hash_enc_data) {
876                         memcpy(request.data.chng_pswd_auth_crap.old_nt_hash_enc,
877                                params->old_password.response.old_nt_hash_enc_data,
878                                request.data.chng_pswd_auth_crap.old_nt_hash_enc_len);
879                         request.data.chng_pswd_auth_crap.old_nt_hash_enc_len =
880                                 params->old_password.response.old_nt_hash_enc_length;
881                 }
882
883                 if (params->old_password.response.old_lm_hash_enc_data) {
884                         memcpy(request.data.chng_pswd_auth_crap.old_lm_hash_enc,
885                                params->old_password.response.old_lm_hash_enc_data,
886                                request.data.chng_pswd_auth_crap.old_lm_hash_enc_len);
887                         request.data.chng_pswd_auth_crap.old_lm_hash_enc_len =
888                                 params->old_password.response.old_lm_hash_enc_length;
889                 }
890
891                 break;
892         default:
893                 wbc_status = WBC_ERR_INVALID_PARAM;
894                 BAIL_ON_WBC_ERROR(wbc_status);
895                 break;
896         }
897
898         /* Send request */
899
900         wbc_status = wbcRequestResponse(cmd,
901                                         &request,
902                                         &response);
903         if (WBC_ERROR_IS_OK(wbc_status)) {
904                 goto done;
905         }
906
907         /* Take the response above and return it to the caller */
908
909         if (response.data.auth.nt_status != 0) {
910                 if (error) {
911                         wbc_status = wbc_create_error_info(&response,
912                                                            error);
913                         BAIL_ON_WBC_ERROR(wbc_status);
914                 }
915
916         }
917
918         if (policy) {
919                 wbc_status = wbc_create_password_policy_info(&response,
920                                                              policy);
921                 BAIL_ON_WBC_ERROR(wbc_status);
922         }
923
924         if (reject_reason) {
925                 *reject_reason = response.data.auth.reject_reason;
926         }
927
928         wbc_status = WBC_ERR_PWD_CHANGE_FAILED;
929         BAIL_ON_WBC_ERROR(wbc_status);
930
931  done:
932         return wbc_status;
933 }
934
935 /* Change a password for a user */
936 wbcErr wbcChangeUserPassword(const char *username,
937                              const char *old_password,
938                              const char *new_password)
939 {
940         wbcErr wbc_status = WBC_ERR_SUCCESS;
941         struct wbcChangePasswordParams params;
942
943         ZERO_STRUCT(params);
944
945         params.account_name             = username;
946         params.level                    = WBC_CHANGE_PASSWORD_LEVEL_PLAIN;
947         params.old_password.plaintext   = old_password;
948         params.new_password.plaintext   = new_password;
949
950         wbc_status = wbcChangeUserPasswordEx(&params,
951                                              NULL,
952                                              NULL,
953                                              NULL);
954         BAIL_ON_WBC_ERROR(wbc_status);
955
956 done:
957         return wbc_status;
958 }
959
960 /* Logon a User */
961 wbcErr wbcLogonUser(const struct wbcLogonUserParams *params,
962                     struct wbcLogonUserInfo **info,
963                     struct wbcAuthErrorInfo **error,
964                     struct wbcUserPasswordPolicyInfo **policy)
965 {
966         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
967         int cmd = 0;
968         struct winbindd_request request;
969         struct winbindd_response response;
970         uint32_t i;
971
972         ZERO_STRUCT(request);
973         ZERO_STRUCT(response);
974
975         if (info) {
976                 *info = NULL;
977         }
978         if (error) {
979                 *error = NULL;
980         }
981         if (policy) {
982                 *policy = NULL;
983         }
984
985         if (!params) {
986                 wbc_status = WBC_ERR_INVALID_PARAM;
987                 BAIL_ON_WBC_ERROR(wbc_status);
988         }
989
990         if (!params->username) {
991                 wbc_status = WBC_ERR_INVALID_PARAM;
992                 BAIL_ON_WBC_ERROR(wbc_status);
993         }
994
995         if ((params->num_blobs > 0) && (params->blobs == NULL)) {
996                 wbc_status = WBC_ERR_INVALID_PARAM;
997                 BAIL_ON_WBC_ERROR(wbc_status);
998         }
999         if ((params->num_blobs == 0) && (params->blobs != NULL)) {
1000                 wbc_status = WBC_ERR_INVALID_PARAM;
1001                 BAIL_ON_WBC_ERROR(wbc_status);
1002         }
1003
1004         /* Initialize request */
1005
1006         cmd = WINBINDD_PAM_AUTH;
1007         request.flags = WBFLAG_PAM_INFO3_TEXT |
1008                         WBFLAG_PAM_USER_SESSION_KEY |
1009                         WBFLAG_PAM_LMKEY;
1010
1011         if (!params->password) {
1012                 wbc_status = WBC_ERR_INVALID_PARAM;
1013                 BAIL_ON_WBC_ERROR(wbc_status);
1014         }
1015
1016         strncpy(request.data.auth.user,
1017                 params->username,
1018                 sizeof(request.data.auth.user)-1);
1019
1020         strncpy(request.data.auth.pass,
1021                 params->password,
1022                 sizeof(request.data.auth.pass)-1);
1023
1024         for (i=0; i<params->num_blobs; i++) {
1025
1026                 if (strcasecmp(params->blobs[i].name, "krb5_cc_type") == 0) {
1027                         if (params->blobs[i].blob.data) {
1028                                 strncpy(request.data.auth.krb5_cc_type,
1029                                         (const char *)params->blobs[i].blob.data,
1030                                         sizeof(request.data.auth.krb5_cc_type) - 1);
1031                         }
1032                         continue;
1033                 }
1034
1035                 if (strcasecmp(params->blobs[i].name, "user_uid") == 0) {
1036                         if (params->blobs[i].blob.data) {
1037                                 memcpy(&request.data.auth.uid,
1038                                         params->blobs[i].blob.data,
1039                                         MIN(sizeof(request.data.auth.uid),
1040                                             params->blobs[i].blob.length));
1041                         }
1042                         continue;
1043                 }
1044
1045                 if (strcasecmp(params->blobs[i].name, "flags") == 0) {
1046                         if (params->blobs[i].blob.data) {
1047                                 uint32_t flags;
1048                                 memcpy(&flags,
1049                                         params->blobs[i].blob.data,
1050                                         MIN(sizeof(flags),
1051                                             params->blobs[i].blob.length));
1052                                 request.flags |= flags;
1053                         }
1054                         continue;
1055                 }
1056
1057                 if (strcasecmp(params->blobs[i].name, "membership_of") == 0) {
1058                         if (params->blobs[i].blob.data &&
1059                             params->blobs[i].blob.data[0] > 0) {
1060                                 strncpy(request.data.auth.require_membership_of_sid,
1061                                         (const char *)params->blobs[i].blob.data,
1062                                         sizeof(request.data.auth.require_membership_of_sid) - 1);
1063                         }
1064                         continue;
1065                 }
1066         }
1067
1068         wbc_status = wbcRequestResponse(cmd,
1069                                         &request,
1070                                         &response);
1071
1072         if (response.data.auth.nt_status != 0) {
1073                 if (error) {
1074                         wbc_status = wbc_create_error_info(&response,
1075                                                            error);
1076                         BAIL_ON_WBC_ERROR(wbc_status);
1077                 }
1078
1079                 wbc_status = WBC_ERR_AUTH_ERROR;
1080                 BAIL_ON_WBC_ERROR(wbc_status);
1081         }
1082         BAIL_ON_WBC_ERROR(wbc_status);
1083
1084         if (info) {
1085                 wbc_status = wbc_create_logon_info(&response,
1086                                                    info);
1087                 BAIL_ON_WBC_ERROR(wbc_status);
1088         }
1089
1090         if (policy) {
1091                 wbc_status = wbc_create_password_policy_info(&response,
1092                                                              policy);
1093                 BAIL_ON_WBC_ERROR(wbc_status);
1094         }
1095
1096 done:
1097         winbindd_free_response(&response);
1098
1099         return wbc_status;
1100 }
1101
1102 /* Authenticate a user with cached credentials */
1103 wbcErr wbcCredentialCache(struct wbcCredentialCacheParams *params,
1104                           struct wbcCredentialCacheInfo **info,
1105                           struct wbcAuthErrorInfo **error)
1106 {
1107         wbcErr status = WBC_ERR_UNKNOWN_FAILURE;
1108         struct wbcCredentialCacheInfo *result = NULL;
1109         struct winbindd_request request;
1110         struct winbindd_response response;
1111         struct wbcNamedBlob *initial_blob = NULL;
1112         struct wbcNamedBlob *challenge_blob = NULL;
1113         int i;
1114
1115         ZERO_STRUCT(request);
1116         ZERO_STRUCT(response);
1117
1118         if (info != NULL) {
1119                 *info = NULL;
1120         }
1121         if (error != NULL) {
1122                 *error = NULL;
1123         }
1124         if ((params == NULL)
1125             || (params->account_name == NULL)
1126             || (params->level != WBC_CREDENTIAL_CACHE_LEVEL_NTLMSSP)) {
1127                 status = WBC_ERR_INVALID_PARAM;
1128                 goto fail;
1129         }
1130
1131         if (params->domain_name != NULL) {
1132                 status = wbcRequestResponse(WINBINDD_INFO, NULL, &response);
1133                 if (!WBC_ERROR_IS_OK(status)) {
1134                         goto fail;
1135                 }
1136                 snprintf(request.data.ccache_ntlm_auth.user,
1137                          sizeof(request.data.ccache_ntlm_auth.user)-1,
1138                          "%s%c%s", params->domain_name,
1139                          response.data.info.winbind_separator,
1140                          params->account_name);
1141         } else {
1142                 strncpy(request.data.ccache_ntlm_auth.user,
1143                         params->account_name,
1144                         sizeof(request.data.ccache_ntlm_auth.user)-1);
1145         }
1146         request.data.ccache_ntlm_auth.uid = getuid();
1147
1148         for (i=0; i<params->num_blobs; i++) {
1149                 if (strcasecmp(params->blobs[i].name, "initial_blob") == 0) {
1150                         initial_blob = &params->blobs[i];
1151                         break;
1152                 }
1153                 if (strcasecmp(params->blobs[i].name, "challenge_blob") == 0) {
1154                         challenge_blob = &params->blobs[i];
1155                         break;
1156                 }
1157         }
1158
1159         request.data.ccache_ntlm_auth.initial_blob_len = 0;
1160         request.data.ccache_ntlm_auth.challenge_blob_len = 0;
1161         request.extra_len = 0;
1162
1163         if (initial_blob != NULL) {
1164                 request.data.ccache_ntlm_auth.initial_blob_len =
1165                         initial_blob->blob.length;
1166                 request.extra_len += initial_blob->blob.length;
1167         }
1168         if (challenge_blob != NULL) {
1169                 request.data.ccache_ntlm_auth.challenge_blob_len =
1170                         challenge_blob->blob.length;
1171                 request.extra_len += challenge_blob->blob.length;
1172         }
1173
1174         if (request.extra_len != 0) {
1175                 request.extra_data.data = talloc_array(
1176                         NULL, char, request.extra_len);
1177                 if (request.extra_data.data == NULL) {
1178                         status = WBC_ERR_NO_MEMORY;
1179                         goto fail;
1180                 }
1181         }
1182         if (initial_blob != NULL) {
1183                 memcpy(request.extra_data.data,
1184                        initial_blob->blob.data, initial_blob->blob.length);
1185         }
1186         if (challenge_blob != NULL) {
1187                 memcpy(request.extra_data.data
1188                        + request.data.ccache_ntlm_auth.initial_blob_len,
1189                        challenge_blob->blob.data,
1190                        challenge_blob->blob.length);
1191         }
1192
1193         status = wbcRequestResponse(WINBINDD_CCACHE_NTLMAUTH, &request,
1194                                     &response);
1195         if (!WBC_ERROR_IS_OK(status)) {
1196                 goto fail;
1197         }
1198
1199         result = talloc(NULL, struct wbcCredentialCacheInfo);
1200         if (result == NULL) {
1201                 status = WBC_ERR_NO_MEMORY;
1202                 goto fail;
1203         }
1204         result->num_blobs = 0;
1205         result->blobs = talloc(result, struct wbcNamedBlob);
1206         if (result->blobs == NULL) {
1207                 status = WBC_ERR_NO_MEMORY;
1208                 goto fail;
1209         }
1210         status = wbcAddNamedBlob(&result->num_blobs, &result->blobs,
1211                                  "auth_blob", 0,
1212                                  (uint8_t *)response.extra_data.data,
1213                                  response.data.ccache_ntlm_auth.auth_blob_len);
1214         if (!WBC_ERROR_IS_OK(status)) {
1215                 goto fail;
1216         }
1217         status = wbcAddNamedBlob(
1218                 &result->num_blobs, &result->blobs, "session_key", 0,
1219                 response.data.ccache_ntlm_auth.session_key,
1220                 sizeof(response.data.ccache_ntlm_auth.session_key));
1221         if (!WBC_ERROR_IS_OK(status)) {
1222                 goto fail;
1223         }
1224
1225         winbindd_free_response(&response);
1226         *info = result;
1227         return WBC_ERR_SUCCESS;
1228
1229 fail:
1230         TALLOC_FREE(request.extra_data.data);
1231         winbindd_free_response(&response);
1232         talloc_free(result);
1233         return status;
1234 }
1235
1236 /* Authenticate a user with cached credentials */
1237 wbcErr wbcCredentialSave(const char *user, const char *password)
1238 {
1239         struct winbindd_request request;
1240         struct winbindd_response response;
1241
1242         ZERO_STRUCT(request);
1243         ZERO_STRUCT(response);
1244
1245         strncpy(request.data.ccache_save.user, user,
1246                 sizeof(request.data.ccache_save.user)-1);
1247         strncpy(request.data.ccache_save.pass, password,
1248                 sizeof(request.data.ccache_save.pass)-1);
1249         request.data.ccache_save.uid = getuid();
1250
1251         return wbcRequestResponse(WINBINDD_CCACHE_SAVE, &request, &response);
1252 }