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