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