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