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