Use pwb_context in pam_winbind.
[nivanova/samba-autobuild/.git] / source3 / nsswitch / pam_winbind.c
1 /* pam_winbind module
2
3    Copyright Andrew Tridgell <tridge@samba.org> 2000
4    Copyright Tim Potter <tpot@samba.org> 2000
5    Copyright Andrew Bartlett <abartlet@samba.org> 2002
6    Copyright Guenther Deschner <gd@samba.org> 2005-2007
7
8    largely based on pam_userdb by Cristian Gafton <gafton@redhat.com> also
9    contains large slabs of code from pam_unix by Elliot Lee
10    <sopwith@redhat.com> (see copyright below for full details)
11 */
12
13 #include "pam_winbind.h"
14
15 #define _PAM_LOG_FUNCTION_ENTER(function, ctx) \
16         do { \
17                 _pam_log_debug(ctx, LOG_DEBUG, "[pamh: %p] ENTER: " \
18                                function " (flags: 0x%04x)", ctx->pamh, ctx->flags); \
19                 _pam_log_state(ctx); \
20         } while (0)
21
22 #define _PAM_LOG_FUNCTION_LEAVE(function, ctx, retval) \
23         do { \
24                 _pam_log_debug(ctx, LOG_DEBUG, "[pamh: %p] LEAVE: " \
25                                function " returning %d", ctx->pamh, retval); \
26                 _pam_log_state(ctx); \
27         } while (0)
28
29 /* data tokens */
30
31 #define MAX_PASSWD_TRIES        3
32
33 /*
34  * Work around the pam API that has functions with void ** as parameters
35  * These lead to strict aliasing warnings with gcc.
36  */
37 static int _pam_get_item(const pam_handle_t *pamh,
38                          int item_type,
39                          const void *_item)
40 {
41         const void **item = (const void **)_item;
42         return pam_get_item(pamh, item_type, item);
43 }
44 static int _pam_get_data(const pam_handle_t *pamh,
45                          const char *module_data_name,
46                          const void *_data)
47 {
48         const void **data = (const void **)_data;
49         return pam_get_data(pamh, module_data_name, data);
50 }
51
52 /* some syslogging */
53
54 #ifdef HAVE_PAM_VSYSLOG
55 static void _pam_log_int(const pam_handle_t *pamh,
56                          int err,
57                          const char *format,
58                          va_list args)
59 {
60         pam_vsyslog(pamh, err, format, args);
61 }
62 #else
63 static void _pam_log_int(const pam_handle_t *pamh,
64                          int err,
65                          const char *format,
66                          va_list args)
67 {
68         char *format2 = NULL;
69         const char *service;
70
71         _pam_get_item(pamh, PAM_SERVICE, &service);
72
73         format2 = (char *)malloc(strlen(MODULE_NAME)+strlen(format)+strlen(service)+5);
74         if (format2 == NULL) {
75                 /* what else todo ? */
76                 vsyslog(err, format, args);
77                 return;
78         }
79
80         sprintf(format2, "%s(%s): %s", MODULE_NAME, service, format);
81         vsyslog(err, format2, args);
82         SAFE_FREE(format2);
83 }
84 #endif /* HAVE_PAM_VSYSLOG */
85
86 static bool _pam_log_is_silent(int ctrl)
87 {
88         return on(ctrl, WINBIND_SILENT);
89 }
90
91 static void _pam_log(struct pwb_context *r, int err, const char *format, ...) PRINTF_ATTRIBUTE(3,4);
92 static void _pam_log(struct pwb_context *r, int err, const char *format, ...)
93 {
94         va_list args;
95
96         if (_pam_log_is_silent(r->ctrl)) {
97                 return;
98         }
99
100         va_start(args, format);
101         _pam_log_int(r->pamh, err, format, args);
102         va_end(args);
103 }
104 static void __pam_log(const pam_handle_t *pamh, int ctrl, int err, const char *format, ...) PRINTF_ATTRIBUTE(4,5);
105 static void __pam_log(const pam_handle_t *pamh, int ctrl, int err, const char *format, ...)
106 {
107         va_list args;
108
109         if (_pam_log_is_silent(ctrl)) {
110                 return;
111         }
112
113         va_start(args, format);
114         _pam_log_int(pamh, err, format, args);
115         va_end(args);
116 }
117
118 static bool _pam_log_is_debug_enabled(int ctrl)
119 {
120         if (ctrl == -1) {
121                 return false;
122         }
123
124         if (_pam_log_is_silent(ctrl)) {
125                 return false;
126         }
127
128         if (!(ctrl & WINBIND_DEBUG_ARG)) {
129                 return false;
130         }
131
132         return true;
133 }
134
135 static bool _pam_log_is_debug_state_enabled(int ctrl)
136 {
137         if (!(ctrl & WINBIND_DEBUG_STATE)) {
138                 return false;
139         }
140
141         return _pam_log_is_debug_enabled(ctrl);
142 }
143
144 static void _pam_log_debug(struct pwb_context *r, int err, const char *format, ...) PRINTF_ATTRIBUTE(3,4);
145 static void _pam_log_debug(struct pwb_context *r, int err, const char *format, ...)
146 {
147         va_list args;
148
149         if (!_pam_log_is_debug_enabled(r->ctrl)) {
150                 return;
151         }
152
153         va_start(args, format);
154         _pam_log_int(r->pamh, err, format, args);
155         va_end(args);
156 }
157 static void __pam_log_debug(const pam_handle_t *pamh, int ctrl, int err, const char *format, ...) PRINTF_ATTRIBUTE(4,5);
158 static void __pam_log_debug(const pam_handle_t *pamh, int ctrl, int err, const char *format, ...)
159 {
160         va_list args;
161
162         if (!_pam_log_is_debug_enabled(ctrl)) {
163                 return;
164         }
165
166         va_start(args, format);
167         _pam_log_int(pamh, err, format, args);
168         va_end(args);
169 }
170
171 static void _pam_log_state_datum(struct pwb_context *ctx,
172                                  int item_type,
173                                  const char *key,
174                                  int is_string)
175 {
176         const void *data = NULL;
177         if (item_type != 0) {
178                 pam_get_item(ctx->pamh, item_type, &data);
179         } else {
180                 pam_get_data(ctx->pamh, key, &data);
181         }
182         if (data != NULL) {
183                 const char *type = (item_type != 0) ? "ITEM" : "DATA";
184                 if (is_string != 0) {
185                         _pam_log_debug(ctx, LOG_DEBUG,
186                                        "[pamh: %p] STATE: %s(%s) = \"%s\" (%p)",
187                                        ctx->pamh, type, key, (const char *)data,
188                                        data);
189                 } else {
190                         _pam_log_debug(ctx, LOG_DEBUG,
191                                        "[pamh: %p] STATE: %s(%s) = %p",
192                                        ctx->pamh, type, key, data);
193                 }
194         }
195 }
196
197 #define _PAM_LOG_STATE_DATA_POINTER(ctx, module_data_name) \
198         _pam_log_state_datum(ctx, 0, module_data_name, 0)
199
200 #define _PAM_LOG_STATE_DATA_STRING(ctx, module_data_name) \
201         _pam_log_state_datum(ctx, 0, module_data_name, 1)
202
203 #define _PAM_LOG_STATE_ITEM_POINTER(ctx, item_type) \
204         _pam_log_state_datum(ctx, item_type, #item_type, 0)
205
206 #define _PAM_LOG_STATE_ITEM_STRING(ctx, item_type) \
207         _pam_log_state_datum(ctx, item_type, #item_type, 1)
208
209 #ifdef DEBUG_PASSWORD
210 #define _LOG_PASSWORD_AS_STRING 1
211 #else
212 #define _LOG_PASSWORD_AS_STRING 0
213 #endif
214
215 #define _PAM_LOG_STATE_ITEM_PASSWORD(ctx, item_type) \
216         _pam_log_state_datum(ctx, item_type, #item_type, \
217                              _LOG_PASSWORD_AS_STRING)
218
219 static void _pam_log_state(struct pwb_context *ctx)
220 {
221         if (!_pam_log_is_debug_state_enabled(ctx->ctrl)) {
222                 return;
223         }
224
225         _PAM_LOG_STATE_ITEM_STRING(ctx, PAM_SERVICE);
226         _PAM_LOG_STATE_ITEM_STRING(ctx, PAM_USER);
227         _PAM_LOG_STATE_ITEM_STRING(ctx, PAM_TTY);
228         _PAM_LOG_STATE_ITEM_STRING(ctx, PAM_RHOST);
229         _PAM_LOG_STATE_ITEM_STRING(ctx, PAM_RUSER);
230         _PAM_LOG_STATE_ITEM_PASSWORD(ctx, PAM_OLDAUTHTOK);
231         _PAM_LOG_STATE_ITEM_PASSWORD(ctx, PAM_AUTHTOK);
232         _PAM_LOG_STATE_ITEM_STRING(ctx, PAM_USER_PROMPT);
233         _PAM_LOG_STATE_ITEM_POINTER(ctx, PAM_CONV);
234 #ifdef PAM_FAIL_DELAY
235         _PAM_LOG_STATE_ITEM_POINTER(ctx, PAM_FAIL_DELAY);
236 #endif
237 #ifdef PAM_REPOSITORY
238         _PAM_LOG_STATE_ITEM_POINTER(ctx, PAM_REPOSITORY);
239 #endif
240
241         _PAM_LOG_STATE_DATA_STRING(ctx, PAM_WINBIND_HOMEDIR);
242         _PAM_LOG_STATE_DATA_STRING(ctx, PAM_WINBIND_LOGONSCRIPT);
243         _PAM_LOG_STATE_DATA_STRING(ctx, PAM_WINBIND_LOGONSERVER);
244         _PAM_LOG_STATE_DATA_STRING(ctx, PAM_WINBIND_PROFILEPATH);
245         _PAM_LOG_STATE_DATA_STRING(ctx,
246                                    PAM_WINBIND_NEW_AUTHTOK_REQD);
247                                    /* Use atoi to get PAM result code */
248         _PAM_LOG_STATE_DATA_STRING(ctx,
249                                    PAM_WINBIND_NEW_AUTHTOK_REQD_DURING_AUTH);
250         _PAM_LOG_STATE_DATA_POINTER(ctx, PAM_WINBIND_PWD_LAST_SET);
251 }
252
253 static int _pam_parse(const pam_handle_t *pamh,
254                       int flags,
255                       int argc,
256                       const char **argv,
257                       dictionary **result_d)
258 {
259         int ctrl = 0;
260         const char *config_file = NULL;
261         int i;
262         const char **v;
263         dictionary *d = NULL;
264
265         if (flags & PAM_SILENT) {
266                 ctrl |= WINBIND_SILENT;
267         }
268
269         for (i=argc,v=argv; i-- > 0; ++v) {
270                 if (!strncasecmp(*v, "config", strlen("config"))) {
271                         ctrl |= WINBIND_CONFIG_FILE;
272                         config_file = v[i];
273                         break;
274                 }
275         }
276
277         if (config_file == NULL) {
278                 config_file = PAM_WINBIND_CONFIG_FILE;
279         }
280
281         d = iniparser_load(config_file);
282         if (d == NULL) {
283                 goto config_from_pam;
284         }
285
286         if (iniparser_getboolean(d, "global:debug", false)) {
287                 ctrl |= WINBIND_DEBUG_ARG;
288         }
289
290         if (iniparser_getboolean(d, "global:debug_state", false)) {
291                 ctrl |= WINBIND_DEBUG_STATE;
292         }
293
294         if (iniparser_getboolean(d, "global:cached_login", false)) {
295                 ctrl |= WINBIND_CACHED_LOGIN;
296         }
297
298         if (iniparser_getboolean(d, "global:krb5_auth", false)) {
299                 ctrl |= WINBIND_KRB5_AUTH;
300         }
301
302         if (iniparser_getboolean(d, "global:silent", false)) {
303                 ctrl |= WINBIND_SILENT;
304         }
305
306         if (iniparser_getstr(d, "global:krb5_ccache_type") != NULL) {
307                 ctrl |= WINBIND_KRB5_CCACHE_TYPE;
308         }
309
310         if ((iniparser_getstr(d, "global:require-membership-of") != NULL) ||
311             (iniparser_getstr(d, "global:require_membership_of") != NULL)) {
312                 ctrl |= WINBIND_REQUIRED_MEMBERSHIP;
313         }
314
315         if (iniparser_getboolean(d, "global:try_first_pass", false)) {
316                 ctrl |= WINBIND_TRY_FIRST_PASS_ARG;
317         }
318
319         if (iniparser_getint(d, "global:warn_pwd_expire", 0)) {
320                 ctrl |= WINBIND_WARN_PWD_EXPIRE;
321         }
322
323 config_from_pam:
324         /* step through arguments */
325         for (i=argc,v=argv; i-- > 0; ++v) {
326
327                 /* generic options */
328                 if (!strcmp(*v,"debug"))
329                         ctrl |= WINBIND_DEBUG_ARG;
330                 else if (!strcasecmp(*v, "debug_state"))
331                         ctrl |= WINBIND_DEBUG_STATE;
332                 else if (!strcasecmp(*v, "silent"))
333                         ctrl |= WINBIND_SILENT;
334                 else if (!strcasecmp(*v, "use_authtok"))
335                         ctrl |= WINBIND_USE_AUTHTOK_ARG;
336                 else if (!strcasecmp(*v, "use_first_pass"))
337                         ctrl |= WINBIND_USE_FIRST_PASS_ARG;
338                 else if (!strcasecmp(*v, "try_first_pass"))
339                         ctrl |= WINBIND_TRY_FIRST_PASS_ARG;
340                 else if (!strcasecmp(*v, "unknown_ok"))
341                         ctrl |= WINBIND_UNKNOWN_OK_ARG;
342                 else if (!strncasecmp(*v, "require_membership_of",
343                                       strlen("require_membership_of")))
344                         ctrl |= WINBIND_REQUIRED_MEMBERSHIP;
345                 else if (!strncasecmp(*v, "require-membership-of",
346                                       strlen("require-membership-of")))
347                         ctrl |= WINBIND_REQUIRED_MEMBERSHIP;
348                 else if (!strcasecmp(*v, "krb5_auth"))
349                         ctrl |= WINBIND_KRB5_AUTH;
350                 else if (!strncasecmp(*v, "krb5_ccache_type",
351                                       strlen("krb5_ccache_type")))
352                         ctrl |= WINBIND_KRB5_CCACHE_TYPE;
353                 else if (!strcasecmp(*v, "cached_login"))
354                         ctrl |= WINBIND_CACHED_LOGIN;
355                 else {
356                         __pam_log(pamh, ctrl, LOG_ERR,
357                                  "pam_parse: unknown option: %s", *v);
358                         return -1;
359                 }
360
361         }
362
363         if (result_d) {
364                 *result_d = d;
365         } else {
366                 if (d) {
367                         iniparser_freedict(d);
368                 }
369         }
370
371         return ctrl;
372 };
373
374 static void _pam_winbind_free_context(struct pwb_context *ctx)
375 {
376         if (ctx->dict) {
377                 iniparser_freedict(ctx->dict);
378         }
379
380         SAFE_FREE(ctx);
381 }
382
383 static int _pam_winbind_init_context(pam_handle_t *pamh,
384                                      int flags,
385                                      int argc,
386                                      const char **argv,
387                                      struct pwb_context **ctx_p)
388 {
389         struct pwb_context *r = NULL;
390
391         r = (struct pwb_context *)malloc(sizeof(struct pwb_context));
392         if (!r) {
393                 return PAM_BUF_ERR;
394         }
395
396         ZERO_STRUCTP(r);
397
398         r->pamh = pamh;
399         r->flags = flags;
400         r->argc = argc;
401         r->argv = argv;
402         r->ctrl = _pam_parse(pamh, flags, argc, argv, &r->dict);
403         if (r->ctrl == -1) {
404                 _pam_winbind_free_context(r);
405                 return PAM_SYSTEM_ERR;
406         }
407
408         *ctx_p = r;
409
410         return PAM_SUCCESS;
411 }
412
413 static void _pam_winbind_cleanup_func(pam_handle_t *pamh,
414                                       void *data,
415                                       int error_status)
416 {
417         int ctrl = _pam_parse(pamh, 0, 0, NULL, NULL);
418         if (_pam_log_is_debug_state_enabled(ctrl)) {
419                 __pam_log_debug(pamh, ctrl, LOG_DEBUG,
420                                "[pamh: %p] CLEAN: cleaning up PAM data %p "
421                                "(error_status = %d)", pamh, data,
422                                error_status);
423         }
424         SAFE_FREE(data);
425 }
426
427
428 static const struct ntstatus_errors {
429         const char *ntstatus_string;
430         const char *error_string;
431 } ntstatus_errors[] = {
432         {"NT_STATUS_OK",
433                 "Success"},
434         {"NT_STATUS_BACKUP_CONTROLLER",
435                 "No primary Domain Controler available"},
436         {"NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND",
437                 "No domain controllers found"},
438         {"NT_STATUS_NO_LOGON_SERVERS",
439                 "No logon servers"},
440         {"NT_STATUS_PWD_TOO_SHORT",
441                 "Password too short"},
442         {"NT_STATUS_PWD_TOO_RECENT",
443                 "The password of this user is too recent to change"},
444         {"NT_STATUS_PWD_HISTORY_CONFLICT",
445                 "Password is already in password history"},
446         {"NT_STATUS_PASSWORD_EXPIRED",
447                 "Your password has expired"},
448         {"NT_STATUS_PASSWORD_MUST_CHANGE",
449                 "You need to change your password now"},
450         {"NT_STATUS_INVALID_WORKSTATION",
451                 "You are not allowed to logon from this workstation"},
452         {"NT_STATUS_INVALID_LOGON_HOURS",
453                 "You are not allowed to logon at this time"},
454         {"NT_STATUS_ACCOUNT_EXPIRED",
455                 "Your account has expired. "
456                 "Please contact your System administrator"}, /* SCNR */
457         {"NT_STATUS_ACCOUNT_DISABLED",
458                 "Your account is disabled. "
459                 "Please contact your System administrator"}, /* SCNR */
460         {"NT_STATUS_ACCOUNT_LOCKED_OUT",
461                 "Your account has been locked. "
462                 "Please contact your System administrator"}, /* SCNR */
463         {"NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT",
464                 "Invalid Trust Account"},
465         {"NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT",
466                 "Invalid Trust Account"},
467         {"NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT",
468                 "Invalid Trust Account"},
469         {"NT_STATUS_ACCESS_DENIED",
470                 "Access is denied"},
471         {NULL, NULL}
472 };
473
474 static const char *_get_ntstatus_error_string(const char *nt_status_string)
475 {
476         int i;
477         for (i=0; ntstatus_errors[i].ntstatus_string != NULL; i++) {
478                 if (!strcasecmp(ntstatus_errors[i].ntstatus_string,
479                                 nt_status_string)) {
480                         return ntstatus_errors[i].error_string;
481                 }
482         }
483         return NULL;
484 }
485
486 /* --- authentication management functions --- */
487
488 /* Attempt a conversation */
489
490 static int converse(const pam_handle_t *pamh,
491                     int nargs,
492                     struct pam_message **message,
493                     struct pam_response **response)
494 {
495         int retval;
496         struct pam_conv *conv;
497
498         retval = _pam_get_item(pamh, PAM_CONV, &conv);
499         if (retval == PAM_SUCCESS) {
500                 retval = conv->conv(nargs,
501                                     (const struct pam_message **)message,
502                                     response, conv->appdata_ptr);
503         }
504
505         return retval; /* propagate error status */
506 }
507
508
509 static int _make_remark(struct pwb_context *ctx,
510                         int type,
511                         const char *text)
512 {
513         int retval = PAM_SUCCESS;
514
515         struct pam_message *pmsg[1], msg[1];
516         struct pam_response *resp;
517
518         if (ctx->flags & WINBIND_SILENT) {
519                 return PAM_SUCCESS;
520         }
521
522         pmsg[0] = &msg[0];
523         msg[0].msg = discard_const_p(char, text);
524         msg[0].msg_style = type;
525
526         resp = NULL;
527         retval = converse(ctx->pamh, 1, pmsg, &resp);
528
529         if (resp) {
530                 _pam_drop_reply(resp, 1);
531         }
532         return retval;
533 }
534
535 static int _make_remark_v(struct pwb_context *ctx,
536                           int type,
537                           const char *format,
538                           va_list args)
539 {
540         char *var;
541         int ret;
542
543         ret = vasprintf(&var, format, args);
544         if (ret < 0) {
545                 _pam_log(ctx, LOG_ERR, "memory allocation failure");
546                 return ret;
547         }
548
549         ret = _make_remark(ctx, type, var);
550         SAFE_FREE(var);
551         return ret;
552 }
553
554 static int _make_remark_format(struct pwb_context *ctx, int type, const char *format, ...) PRINTF_ATTRIBUTE(3,4);
555 static int _make_remark_format(struct pwb_context *ctx, int type, const char *format, ...)
556 {
557         int ret;
558         va_list args;
559
560         va_start(args, format);
561         ret = _make_remark_v(ctx, type, format, args);
562         va_end(args);
563         return ret;
564 }
565
566 static int pam_winbind_request(struct pwb_context *ctx,
567                                enum winbindd_cmd req_type,
568                                struct winbindd_request *request,
569                                struct winbindd_response *response)
570 {
571         /* Fill in request and send down pipe */
572         winbindd_init_request(request, req_type);
573
574         if (winbind_write_sock(request, sizeof(*request), 0, 0) == -1) {
575                 _pam_log(ctx, LOG_ERR,
576                          "pam_winbind_request: write to socket failed!");
577                 winbind_close_sock();
578                 return PAM_SERVICE_ERR;
579         }
580
581         /* Wait for reply */
582         if (winbindd_read_reply(response) == -1) {
583                 _pam_log(ctx, LOG_ERR,
584                          "pam_winbind_request: read from socket failed!");
585                 winbind_close_sock();
586                 return PAM_SERVICE_ERR;
587         }
588
589         /* We are done with the socket - close it and avoid mischeif */
590         winbind_close_sock();
591
592         /* Copy reply data from socket */
593         if (response->result == WINBINDD_OK) {
594                 return PAM_SUCCESS;
595         }
596
597         /* no need to check for pam_error codes for getpwnam() */
598         switch (req_type) {
599
600                 case WINBINDD_GETPWNAM:
601                 case WINBINDD_LOOKUPNAME:
602                         if (strlen(response->data.auth.nt_status_string) > 0) {
603                                 _pam_log(ctx, LOG_ERR,
604                                          "request failed, NT error was %s",
605                                          response->data.auth.nt_status_string);
606                         } else {
607                                 _pam_log(ctx, LOG_ERR, "request failed");
608                         }
609                         return PAM_USER_UNKNOWN;
610                 default:
611                         break;
612         }
613
614         if (response->data.auth.pam_error != PAM_SUCCESS) {
615                 _pam_log(ctx, LOG_ERR,
616                          "request failed: %s, "
617                          "PAM error was %s (%d), NT error was %s",
618                          response->data.auth.error_string,
619                          pam_strerror(ctx->pamh, response->data.auth.pam_error),
620                          response->data.auth.pam_error,
621                          response->data.auth.nt_status_string);
622                 return response->data.auth.pam_error;
623         }
624
625         _pam_log(ctx, LOG_ERR, "request failed, but PAM error 0!");
626
627         return PAM_SERVICE_ERR;
628 }
629
630 static int pam_winbind_request_log(struct pwb_context *ctx,
631                                    enum winbindd_cmd req_type,
632                                    struct winbindd_request *request,
633                                    struct winbindd_response *response,
634                                    const char *user)
635 {
636         int retval;
637
638         retval = pam_winbind_request(ctx, req_type, request, response);
639
640         switch (retval) {
641         case PAM_AUTH_ERR:
642                 /* incorrect password */
643                 _pam_log(ctx, LOG_WARNING, "user '%s' denied access "
644                          "(incorrect password or invalid membership)", user);
645                 return retval;
646         case PAM_ACCT_EXPIRED:
647                 /* account expired */
648                 _pam_log(ctx, LOG_WARNING, "user '%s' account expired",
649                          user);
650                 return retval;
651         case PAM_AUTHTOK_EXPIRED:
652                 /* password expired */
653                 _pam_log(ctx, LOG_WARNING, "user '%s' password expired",
654                          user);
655                 return retval;
656         case PAM_NEW_AUTHTOK_REQD:
657                 /* new password required */
658                 _pam_log(ctx, LOG_WARNING, "user '%s' new password "
659                          "required", user);
660                 return retval;
661         case PAM_USER_UNKNOWN:
662                 /* the user does not exist */
663                 _pam_log_debug(ctx, LOG_NOTICE, "user '%s' not found",
664                                user);
665                 if (ctx->ctrl & WINBIND_UNKNOWN_OK_ARG) {
666                         return PAM_IGNORE;
667                 }
668                 return retval;
669         case PAM_SUCCESS:
670                 /* Otherwise, the authentication looked good */
671                 switch (req_type) {
672                         case WINBINDD_INFO:
673                                 break;
674                         case WINBINDD_PAM_AUTH:
675                                 _pam_log(ctx, LOG_NOTICE,
676                                          "user '%s' granted access", user);
677                                 break;
678                         case WINBINDD_PAM_CHAUTHTOK:
679                                 _pam_log(ctx, LOG_NOTICE,
680                                          "user '%s' password changed", user);
681                                 break;
682                         default:
683                                 _pam_log(ctx, LOG_NOTICE,
684                                          "user '%s' OK", user);
685                                 break;
686                 }
687
688                 return retval;
689         default:
690                 /* we don't know anything about this return value */
691                 _pam_log(ctx, LOG_ERR,
692                          "internal module error (retval = %d, user = '%s')",
693                          retval, user);
694                 return retval;
695         }
696 }
697
698 /**
699  * send a password expiry message if required
700  *
701  * @param pamh PAM handle
702  * @param ctrl PAM winbind options.
703  * @param next_change expected (calculated) next expiry date.
704  * @param already_expired pointer to a boolean to indicate if the password is
705  *        already expired.
706  *
707  * @return boolean Returns true if message has been sent, false if not.
708  */
709
710 static bool _pam_send_password_expiry_message(struct pwb_context *ctx,
711                                               time_t next_change,
712                                               time_t now,
713                                               int warn_pwd_expire,
714                                               bool *already_expired)
715 {
716         int days = 0;
717         struct tm tm_now, tm_next_change;
718
719         if (already_expired) {
720                 *already_expired = false;
721         }
722
723         if (next_change <= now) {
724                 PAM_WB_REMARK_DIRECT(ctx, "NT_STATUS_PASSWORD_EXPIRED");
725                 if (already_expired) {
726                         *already_expired = true;
727                 }
728                 return true;
729         }
730
731         if ((next_change < 0) ||
732             (next_change > now + warn_pwd_expire * SECONDS_PER_DAY)) {
733                 return false;
734         }
735
736         if ((localtime_r(&now, &tm_now) == NULL) ||
737             (localtime_r(&next_change, &tm_next_change) == NULL)) {
738                 return false;
739         }
740
741         days = (tm_next_change.tm_yday+tm_next_change.tm_year*365) -
742                (tm_now.tm_yday+tm_now.tm_year*365);
743
744         if (days == 0) {
745                 _make_remark(ctx, PAM_TEXT_INFO,
746                              "Your password expires today");
747                 return true;
748         }
749
750         if (days > 0 && days < warn_pwd_expire) {
751                 _make_remark_format(ctx, PAM_TEXT_INFO,
752                                     "Your password will expire in %d %s",
753                                     days, (days > 1) ? "days":"day");
754                 return true;
755         }
756
757         return false;
758 }
759
760 /**
761  * Send a warning if the password expires in the near future
762  *
763  * @param pamh PAM handle
764  * @param ctrl PAM winbind options.
765  * @param response The full authentication response structure.
766  * @param already_expired boolean, is the pwd already expired?
767  *
768  * @return void.
769  */
770
771 static void _pam_warn_password_expiry(struct pwb_context *ctx,
772                                       const struct winbindd_response *response,
773                                       int warn_pwd_expire,
774                                       bool *already_expired)
775 {
776         time_t now = time(NULL);
777         time_t next_change = 0;
778
779         if (already_expired) {
780                 *already_expired = false;
781         }
782
783         /* accounts with ACB_PWNOEXP set never receive a warning */
784         if (response->data.auth.info3.acct_flags & ACB_PWNOEXP) {
785                 return;
786         }
787
788         /* no point in sending a warning if this is a grace logon */
789         if (PAM_WB_GRACE_LOGON(response->data.auth.info3.user_flgs)) {
790                 return;
791         }
792
793         /* check if the info3 must change timestamp has been set */
794         next_change = response->data.auth.info3.pass_must_change_time;
795
796         if (_pam_send_password_expiry_message(ctx, next_change, now,
797                                               warn_pwd_expire,
798                                               already_expired)) {
799                 return;
800         }
801
802         /* now check for the global password policy */
803         /* good catch from Ralf Haferkamp: an expiry of "never" is translated
804          * to -1 */
805         if (response->data.auth.policy.expire <= 0) {
806                 return;
807         }
808
809         next_change = response->data.auth.info3.pass_last_set_time +
810                       response->data.auth.policy.expire;
811
812         if (_pam_send_password_expiry_message(ctx, next_change, now,
813                                               warn_pwd_expire,
814                                               already_expired)) {
815                 return;
816         }
817
818         /* no warning sent */
819 }
820
821 #define IS_SID_STRING(name) (strncmp("S-", name, 2) == 0)
822
823 /**
824  * Append a string, making sure not to overflow and to always return a
825  * NULL-terminated string.
826  *
827  * @param dest Destination string buffer (must already be NULL-terminated).
828  * @param src Source string buffer.
829  * @param dest_buffer_size Size of dest buffer in bytes.
830  *
831  * @return false if dest buffer is not big enough (no bytes copied), true on
832  * success.
833  */
834
835 static bool safe_append_string(char *dest,
836                                const char *src,
837                                int dest_buffer_size)
838 {
839         int dest_length = strlen(dest);
840         int src_length = strlen(src);
841
842         if (dest_length + src_length + 1 > dest_buffer_size) {
843                 return false;
844         }
845
846         memcpy(dest + dest_length, src, src_length + 1);
847         return true;
848 }
849
850 /**
851  * Convert a names into a SID string, appending it to a buffer.
852  *
853  * @param pamh PAM handle
854  * @param ctrl PAM winbind options.
855  * @param user User in PAM request.
856  * @param name Name to convert.
857  * @param sid_list_buffer Where to append the string sid.
858  * @param sid_list_buffer Size of sid_list_buffer (in bytes).
859  *
860  * @return false on failure, true on success.
861  */
862 static bool winbind_name_to_sid_string(struct pwb_context *ctx,
863                                        const char *user,
864                                        const char *name,
865                                        char *sid_list_buffer,
866                                        int sid_list_buffer_size)
867 {
868         const char* sid_string;
869         struct winbindd_response sid_response;
870
871         /* lookup name? */
872         if (IS_SID_STRING(name)) {
873                 sid_string = name;
874         } else {
875                 struct winbindd_request sid_request;
876
877                 ZERO_STRUCT(sid_request);
878                 ZERO_STRUCT(sid_response);
879
880                 _pam_log_debug(ctx, LOG_DEBUG,
881                                "no sid given, looking up: %s\n", name);
882
883                 /* fortunatly winbindd can handle non-separated names */
884                 strncpy(sid_request.data.name.name, name,
885                         sizeof(sid_request.data.name.name) - 1);
886
887                 if (pam_winbind_request_log(ctx, WINBINDD_LOOKUPNAME,
888                                             &sid_request, &sid_response,
889                                             user)) {
890                         _pam_log(ctx, LOG_INFO,
891                                  "could not lookup name: %s\n", name);
892                         return false;
893                 }
894
895                 sid_string = sid_response.data.sid.sid;
896         }
897
898         if (!safe_append_string(sid_list_buffer, sid_string,
899                                 sid_list_buffer_size)) {
900                 return false;
901         }
902
903         return true;
904 }
905
906 /**
907  * Convert a list of names into a list of sids.
908  *
909  * @param pamh PAM handle
910  * @param ctrl PAM winbind options.
911  * @param user User in PAM request.
912  * @param name_list List of names or string sids, separated by commas.
913  * @param sid_list_buffer Where to put the list of string sids.
914  * @param sid_list_buffer Size of sid_list_buffer (in bytes).
915  *
916  * @return false on failure, true on success.
917  */
918 static bool winbind_name_list_to_sid_string_list(struct pwb_context *ctx,
919                                                  const char *user,
920                                                  const char *name_list,
921                                                  char *sid_list_buffer,
922                                                  int sid_list_buffer_size)
923 {
924         bool result = false;
925         char *current_name = NULL;
926         const char *search_location;
927         const char *comma;
928
929         if (sid_list_buffer_size > 0) {
930                 sid_list_buffer[0] = 0;
931         }
932
933         search_location = name_list;
934         while ((comma = strstr(search_location, ",")) != NULL) {
935                 current_name = strndup(search_location,
936                                        comma - search_location);
937                 if (NULL == current_name) {
938                         goto out;
939                 }
940
941                 if (!winbind_name_to_sid_string(ctx, user,
942                                                 current_name,
943                                                 sid_list_buffer,
944                                                 sid_list_buffer_size)) {
945                         goto out;
946                 }
947
948                 SAFE_FREE(current_name);
949
950                 if (!safe_append_string(sid_list_buffer, ",",
951                                         sid_list_buffer_size)) {
952                         goto out;
953                 }
954
955                 search_location = comma + 1;
956         }
957
958         if (!winbind_name_to_sid_string(ctx, user, search_location,
959                                         sid_list_buffer,
960                                         sid_list_buffer_size)) {
961                 goto out;
962         }
963
964         result = true;
965
966 out:
967         SAFE_FREE(current_name);
968         return result;
969 }
970
971 /**
972  * put krb5ccname variable into environment
973  *
974  * @param pamh PAM handle
975  * @param ctrl PAM winbind options.
976  * @param krb5ccname env variable retrieved from winbindd.
977  *
978  * @return void.
979  */
980
981 static void _pam_setup_krb5_env(struct pwb_context *ctx,
982                                 const char *krb5ccname)
983 {
984         char var[PATH_MAX];
985         int ret;
986
987         if (off(ctx->ctrl, WINBIND_KRB5_AUTH)) {
988                 return;
989         }
990
991         if (!krb5ccname || (strlen(krb5ccname) == 0)) {
992                 return;
993         }
994
995         _pam_log_debug(ctx, LOG_DEBUG,
996                        "request returned KRB5CCNAME: %s", krb5ccname);
997
998         if (snprintf(var, sizeof(var), "KRB5CCNAME=%s", krb5ccname) == -1) {
999                 return;
1000         }
1001
1002         ret = pam_putenv(ctx->pamh, var);
1003         if (ret) {
1004                 _pam_log(ctx, LOG_ERR,
1005                          "failed to set KRB5CCNAME to %s: %s",
1006                          var, pam_strerror(ctx->pamh, ret));
1007         }
1008 }
1009
1010 /**
1011  * Set string into the PAM stack.
1012  *
1013  * @param pamh PAM handle
1014  * @param ctrl PAM winbind options.
1015  * @param data_name Key name for pam_set_data.
1016  * @param value String value.
1017  *
1018  * @return void.
1019  */
1020
1021 static void _pam_set_data_string(struct pwb_context *ctx,
1022                                  const char *data_name,
1023                                  const char *value)
1024 {
1025         int ret;
1026
1027         if (!data_name || !value || (strlen(data_name) == 0) ||
1028              (strlen(value) == 0)) {
1029                 return;
1030         }
1031
1032         ret = pam_set_data(ctx->pamh, data_name, (void *)strdup(value),
1033                            _pam_winbind_cleanup_func);
1034         if (ret) {
1035                 _pam_log_debug(ctx, LOG_DEBUG,
1036                                "Could not set data %s: %s\n",
1037                                data_name, pam_strerror(ctx->pamh, ret));
1038         }
1039
1040 }
1041
1042 /**
1043  * Set info3 strings into the PAM stack.
1044  *
1045  * @param pamh PAM handle
1046  * @param ctrl PAM winbind options.
1047  * @param data_name Key name for pam_set_data.
1048  * @param value String value.
1049  *
1050  * @return void.
1051  */
1052
1053 static void _pam_set_data_info3(struct pwb_context *ctx,
1054                                 struct winbindd_response *response)
1055 {
1056         _pam_set_data_string(ctx, PAM_WINBIND_HOMEDIR,
1057                              response->data.auth.info3.home_dir);
1058         _pam_set_data_string(ctx, PAM_WINBIND_LOGONSCRIPT,
1059                              response->data.auth.info3.logon_script);
1060         _pam_set_data_string(ctx, PAM_WINBIND_LOGONSERVER,
1061                              response->data.auth.info3.logon_srv);
1062         _pam_set_data_string(ctx, PAM_WINBIND_PROFILEPATH,
1063                              response->data.auth.info3.profile_path);
1064 }
1065
1066 /**
1067  * Free info3 strings in the PAM stack.
1068  *
1069  * @param pamh PAM handle
1070  *
1071  * @return void.
1072  */
1073
1074 static void _pam_free_data_info3(pam_handle_t *pamh)
1075 {
1076         pam_set_data(pamh, PAM_WINBIND_HOMEDIR, NULL, NULL);
1077         pam_set_data(pamh, PAM_WINBIND_LOGONSCRIPT, NULL, NULL);
1078         pam_set_data(pamh, PAM_WINBIND_LOGONSERVER, NULL, NULL);
1079         pam_set_data(pamh, PAM_WINBIND_PROFILEPATH, NULL, NULL);
1080 }
1081
1082 /**
1083  * Send PAM_ERROR_MSG for cached or grace logons.
1084  *
1085  * @param pamh PAM handle
1086  * @param ctrl PAM winbind options.
1087  * @param username User in PAM request.
1088  * @param info3_user_flgs Info3 flags containing logon type bits.
1089  *
1090  * @return void.
1091  */
1092
1093 static void _pam_warn_logon_type(struct pwb_context *ctx,
1094                                  const char *username,
1095                                  uint32_t info3_user_flgs)
1096 {
1097         /* inform about logon type */
1098         if (PAM_WB_GRACE_LOGON(info3_user_flgs)) {
1099
1100                 _make_remark(ctx, PAM_ERROR_MSG,
1101                              "Grace login. "
1102                              "Please change your password as soon you're "
1103                              "online again");
1104                 _pam_log_debug(ctx, LOG_DEBUG,
1105                                "User %s logged on using grace logon\n",
1106                                username);
1107
1108         } else if (PAM_WB_CACHED_LOGON(info3_user_flgs)) {
1109
1110                 _make_remark(ctx, PAM_ERROR_MSG,
1111                              "Domain Controller unreachable, "
1112                              "using cached credentials instead. "
1113                              "Network resources may be unavailable");
1114                 _pam_log_debug(ctx, LOG_DEBUG,
1115                                "User %s logged on using cached credentials\n",
1116                                username);
1117         }
1118 }
1119
1120 /**
1121  * Send PAM_ERROR_MSG for krb5 errors.
1122  *
1123  * @param pamh PAM handle
1124  * @param ctrl PAM winbind options.
1125  * @param username User in PAM request.
1126  * @param info3_user_flgs Info3 flags containing logon type bits.
1127  *
1128  * @return void.
1129  */
1130
1131 static void _pam_warn_krb5_failure(struct pwb_context *ctx,
1132                                    const char *username,
1133                                    uint32_t info3_user_flgs)
1134 {
1135         if (PAM_WB_KRB5_CLOCK_SKEW(info3_user_flgs)) {
1136                 _make_remark(ctx, PAM_ERROR_MSG,
1137                              "Failed to establish your Kerberos Ticket cache "
1138                              "due time differences\n"
1139                              "with the domain controller.  "
1140                              "Please verify the system time.\n");
1141                 _pam_log_debug(ctx, LOG_DEBUG,
1142                                "User %s: Clock skew when getting Krb5 TGT\n",
1143                                username);
1144         }
1145 }
1146
1147 /**
1148  * Compose Password Restriction String for a PAM_ERROR_MSG conversation.
1149  *
1150  * @param response The struct winbindd_response.
1151  *
1152  * @return string (caller needs to free).
1153  */
1154
1155 static char *_pam_compose_pwd_restriction_string(struct winbindd_response *response)
1156 {
1157         char *str = NULL;
1158         size_t offset = 0, ret = 0, str_size = 1024;
1159
1160         str = (char *)malloc(str_size);
1161         if (!str) {
1162                 return NULL;
1163         }
1164
1165         memset(str, '\0', str_size);
1166
1167         offset = snprintf(str, str_size, "Your password ");
1168         if (offset == -1) {
1169                 goto failed;
1170         }
1171
1172         if (response->data.auth.policy.min_length_password > 0) {
1173                 ret = snprintf(str+offset, str_size-offset,
1174                                "must be at least %d characters; ",
1175                                response->data.auth.policy.min_length_password);
1176                 if (ret == -1) {
1177                         goto failed;
1178                 }
1179                 offset += ret;
1180         }
1181
1182         if (response->data.auth.policy.password_history > 0) {
1183                 ret = snprintf(str+offset, str_size-offset,
1184                                "cannot repeat any of your previous %d "
1185                                "passwords; ",
1186                                response->data.auth.policy.password_history);
1187                 if (ret == -1) {
1188                         goto failed;
1189                 }
1190                 offset += ret;
1191         }
1192
1193         if (response->data.auth.policy.password_properties &
1194             DOMAIN_PASSWORD_COMPLEX) {
1195                 ret = snprintf(str+offset, str_size-offset,
1196                                "must contain capitals, numerals "
1197                                "or punctuation; "
1198                                "and cannot contain your account "
1199                                "or full name; ");
1200                 if (ret == -1) {
1201                         goto failed;
1202                 }
1203                 offset += ret;
1204         }
1205
1206         ret = snprintf(str+offset, str_size-offset,
1207                        "Please type a different password. "
1208                        "Type a password which meets these requirements in "
1209                        "both text boxes.");
1210         if (ret == -1) {
1211                 goto failed;
1212         }
1213
1214         return str;
1215
1216  failed:
1217         SAFE_FREE(str);
1218         return NULL;
1219 }
1220
1221 /* talk to winbindd */
1222 static int winbind_auth_request(struct pwb_context *ctx,
1223                                 const char *user,
1224                                 const char *pass,
1225                                 const char *member,
1226                                 const char *cctype,
1227                                 const int warn_pwd_expire,
1228                                 struct winbindd_response *p_response,
1229                                 time_t *pwd_last_set,
1230                                 char **user_ret)
1231 {
1232         struct winbindd_request request;
1233         struct winbindd_response response;
1234         int ret;
1235         bool already_expired = false;
1236
1237         ZERO_STRUCT(request);
1238         ZERO_STRUCT(response);
1239
1240         if (pwd_last_set) {
1241                 *pwd_last_set = 0;
1242         }
1243
1244         strncpy(request.data.auth.user, user,
1245                 sizeof(request.data.auth.user)-1);
1246
1247         strncpy(request.data.auth.pass, pass,
1248                 sizeof(request.data.auth.pass)-1);
1249
1250         request.data.auth.krb5_cc_type[0] = '\0';
1251         request.data.auth.uid = -1;
1252
1253         request.flags = WBFLAG_PAM_INFO3_TEXT | WBFLAG_PAM_GET_PWD_POLICY;
1254
1255         /* Krb5 auth always has to go against the KDC of the user's realm */
1256
1257         if (ctx->ctrl & WINBIND_KRB5_AUTH) {
1258                 request.flags |= WBFLAG_PAM_CONTACT_TRUSTDOM;
1259         }
1260
1261         if (ctx->ctrl & (WINBIND_KRB5_AUTH|WINBIND_CACHED_LOGIN)) {
1262                 struct passwd *pwd = NULL;
1263
1264                 pwd = getpwnam(user);
1265                 if (pwd == NULL) {
1266                         return PAM_USER_UNKNOWN;
1267                 }
1268                 request.data.auth.uid = pwd->pw_uid;
1269         }
1270
1271         if (ctx->ctrl & WINBIND_KRB5_AUTH) {
1272
1273                 _pam_log_debug(ctx, LOG_DEBUG,
1274                                "enabling krb5 login flag\n");
1275
1276                 request.flags |= WBFLAG_PAM_KRB5 |
1277                                  WBFLAG_PAM_FALLBACK_AFTER_KRB5;
1278         }
1279
1280         if (ctx->ctrl & WINBIND_CACHED_LOGIN) {
1281                 _pam_log_debug(ctx, LOG_DEBUG,
1282                                "enabling cached login flag\n");
1283                 request.flags |= WBFLAG_PAM_CACHED_LOGIN;
1284         }
1285
1286         if (user_ret) {
1287                 *user_ret = NULL;
1288                 request.flags |= WBFLAG_PAM_UNIX_NAME;
1289         }
1290
1291         if (cctype != NULL) {
1292                 strncpy(request.data.auth.krb5_cc_type, cctype,
1293                         sizeof(request.data.auth.krb5_cc_type) - 1);
1294                 _pam_log_debug(ctx, LOG_DEBUG,
1295                                "enabling request for a %s krb5 ccache\n",
1296                                cctype);
1297         }
1298
1299         request.data.auth.require_membership_of_sid[0] = '\0';
1300
1301         if (member != NULL) {
1302
1303                 if (!winbind_name_list_to_sid_string_list(ctx, user,
1304                         member,
1305                         request.data.auth.require_membership_of_sid,
1306                         sizeof(request.data.auth.require_membership_of_sid))) {
1307
1308                         _pam_log_debug(ctx, LOG_ERR,
1309                                        "failed to serialize membership of sid "
1310                                        "\"%s\"\n", member);
1311                         return PAM_AUTH_ERR;
1312                 }
1313         }
1314
1315         ret = pam_winbind_request_log(ctx, WINBINDD_PAM_AUTH,
1316                                       &request, &response, user);
1317
1318         if (pwd_last_set) {
1319                 *pwd_last_set = response.data.auth.info3.pass_last_set_time;
1320         }
1321
1322         if (p_response) {
1323                 /* We want to process the response in the caller. */
1324                 *p_response = response;
1325                 return ret;
1326         }
1327
1328         if (ret) {
1329                 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1330                                                  "NT_STATUS_PASSWORD_EXPIRED");
1331                 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1332                                                  "NT_STATUS_PASSWORD_MUST_CHANGE");
1333                 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1334                                                  "NT_STATUS_INVALID_WORKSTATION");
1335                 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1336                                                  "NT_STATUS_INVALID_LOGON_HOURS");
1337                 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1338                                                  "NT_STATUS_ACCOUNT_EXPIRED");
1339                 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1340                                                  "NT_STATUS_ACCOUNT_DISABLED");
1341                 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1342                                                  "NT_STATUS_ACCOUNT_LOCKED_OUT");
1343                 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1344                                                  "NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT");
1345                 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1346                                                  "NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT");
1347                 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1348                                                  "NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT");
1349                 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1350                                                  "NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND");
1351                 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1352                                                  "NT_STATUS_NO_LOGON_SERVERS");
1353                 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1354                                                  "NT_STATUS_WRONG_PASSWORD");
1355                 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1356                                                  "NT_STATUS_ACCESS_DENIED");
1357         }
1358
1359         if (ret == PAM_SUCCESS) {
1360
1361                 /* warn a user if the password is about to expire soon */
1362                 _pam_warn_password_expiry(ctx, &response,
1363                                           warn_pwd_expire,
1364                                           &already_expired);
1365
1366                 if (already_expired == true) {
1367                         SMB_TIME_T last_set;
1368                         last_set = response.data.auth.info3.pass_last_set_time;
1369                         _pam_log_debug(ctx, LOG_DEBUG,
1370                                        "Password has expired "
1371                                        "(Password was last set: %lld, "
1372                                        "the policy says it should expire here "
1373                                        "%lld (now it's: %lu))\n",
1374                                        (long long int)last_set,
1375                                        (long long int)last_set +
1376                                        response.data.auth.policy.expire,
1377                                        time(NULL));
1378
1379                         return PAM_AUTHTOK_EXPIRED;
1380                 }
1381
1382                 /* inform about logon type */
1383                 _pam_warn_logon_type(ctx, user,
1384                                      response.data.auth.info3.user_flgs);
1385
1386                 /* inform about krb5 failures */
1387                 _pam_warn_krb5_failure(ctx, user,
1388                                        response.data.auth.info3.user_flgs);
1389
1390                 /* set some info3 info for other modules in the stack */
1391                 _pam_set_data_info3(ctx, &response);
1392
1393                 /* put krb5ccname into env */
1394                 _pam_setup_krb5_env(ctx, response.data.auth.krb5ccname);
1395
1396                 /* If winbindd returned a username, return the pointer to it
1397                  * here. */
1398                 if (user_ret && response.data.auth.unix_username[0]) {
1399                         /* We have to trust it's a null terminated string. */
1400                         *user_ret = strndup(response.data.auth.unix_username,
1401                                     sizeof(response.data.auth.unix_username) - 1);
1402                 }
1403         }
1404
1405         return ret;
1406 }
1407
1408 /* talk to winbindd */
1409 static int winbind_chauthtok_request(struct pwb_context *ctx,
1410                                      const char *user,
1411                                      const char *oldpass,
1412                                      const char *newpass,
1413                                      time_t pwd_last_set)
1414 {
1415         struct winbindd_request request;
1416         struct winbindd_response response;
1417         int ret;
1418
1419         ZERO_STRUCT(request);
1420         ZERO_STRUCT(response);
1421
1422         if (request.data.chauthtok.user == NULL) {
1423                 return -2;
1424         }
1425
1426         strncpy(request.data.chauthtok.user, user,
1427                 sizeof(request.data.chauthtok.user) - 1);
1428
1429         if (oldpass != NULL) {
1430                 strncpy(request.data.chauthtok.oldpass, oldpass,
1431                         sizeof(request.data.chauthtok.oldpass) - 1);
1432         } else {
1433                 request.data.chauthtok.oldpass[0] = '\0';
1434         }
1435
1436         if (newpass != NULL) {
1437                 strncpy(request.data.chauthtok.newpass, newpass,
1438                         sizeof(request.data.chauthtok.newpass) - 1);
1439         } else {
1440                 request.data.chauthtok.newpass[0] = '\0';
1441         }
1442
1443         if (ctx->ctrl & WINBIND_KRB5_AUTH) {
1444                 request.flags = WBFLAG_PAM_KRB5 |
1445                                 WBFLAG_PAM_CONTACT_TRUSTDOM;
1446         }
1447
1448         ret = pam_winbind_request_log(ctx, WINBINDD_PAM_CHAUTHTOK,
1449                                       &request, &response, user);
1450
1451         if (ret == PAM_SUCCESS) {
1452                 return ret;
1453         }
1454
1455         PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1456                                          "NT_STATUS_BACKUP_CONTROLLER");
1457         PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1458                                          "NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND");
1459         PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1460                                          "NT_STATUS_NO_LOGON_SERVERS");
1461         PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1462                                          "NT_STATUS_ACCESS_DENIED");
1463
1464         /* TODO: tell the min pwd length ? */
1465         PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1466                                          "NT_STATUS_PWD_TOO_SHORT");
1467
1468         /* TODO: tell the minage ? */
1469         PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1470                                          "NT_STATUS_PWD_TOO_RECENT");
1471
1472         /* TODO: tell the history length ? */
1473         PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1474                                          "NT_STATUS_PWD_HISTORY_CONFLICT");
1475
1476         if (!strcasecmp(response.data.auth.nt_status_string,
1477                         "NT_STATUS_PASSWORD_RESTRICTION")) {
1478
1479                 char *pwd_restriction_string = NULL;
1480                 SMB_TIME_T min_pwd_age;
1481                 uint32_t reject_reason = response.data.auth.reject_reason;
1482                 min_pwd_age = response.data.auth.policy.min_passwordage;
1483
1484                 /* FIXME: avoid to send multiple PAM messages after another */
1485                 switch (reject_reason) {
1486                         case -1:
1487                                 break;
1488                         case SAMR_REJECT_OTHER:
1489                                 if ((min_pwd_age > 0) &&
1490                                     (pwd_last_set + min_pwd_age > time(NULL))) {
1491                                         PAM_WB_REMARK_DIRECT(ctx,
1492                                              "NT_STATUS_PWD_TOO_RECENT");
1493                                 }
1494                                 break;
1495                         case SAMR_REJECT_TOO_SHORT:
1496                                 PAM_WB_REMARK_DIRECT(ctx,
1497                                         "NT_STATUS_PWD_TOO_SHORT");
1498                                 break;
1499                         case SAMR_REJECT_IN_HISTORY:
1500                                 PAM_WB_REMARK_DIRECT(ctx,
1501                                         "NT_STATUS_PWD_HISTORY_CONFLICT");
1502                                 break;
1503                         case SAMR_REJECT_COMPLEXITY:
1504                                 _make_remark(ctx, PAM_ERROR_MSG,
1505                                              "Password does not meet "
1506                                              "complexity requirements");
1507                                 break;
1508                         default:
1509                                 _pam_log_debug(ctx, LOG_DEBUG,
1510                                                "unknown password change "
1511                                                "reject reason: %d",
1512                                                reject_reason);
1513                                 break;
1514                 }
1515
1516                 pwd_restriction_string =
1517                         _pam_compose_pwd_restriction_string(&response);
1518                 if (pwd_restriction_string) {
1519                         _make_remark(ctx, PAM_ERROR_MSG,
1520                                      pwd_restriction_string);
1521                         SAFE_FREE(pwd_restriction_string);
1522                 }
1523         }
1524
1525         return ret;
1526 }
1527
1528 /*
1529  * Checks if a user has an account
1530  *
1531  * return values:
1532  *       1  = User not found
1533  *       0  = OK
1534  *      -1  = System error
1535  */
1536 static int valid_user(struct pwb_context *ctx,
1537                       const char *user)
1538 {
1539         /* check not only if the user is available over NSS calls, also make
1540          * sure it's really a winbind user, this is important when stacking PAM
1541          * modules in the 'account' or 'password' facility. */
1542
1543         struct passwd *pwd = NULL;
1544         struct winbindd_request request;
1545         struct winbindd_response response;
1546         int ret;
1547
1548         ZERO_STRUCT(request);
1549         ZERO_STRUCT(response);
1550
1551         pwd = getpwnam(user);
1552         if (pwd == NULL) {
1553                 return 1;
1554         }
1555
1556         strncpy(request.data.username, user,
1557                 sizeof(request.data.username) - 1);
1558
1559         ret = pam_winbind_request_log(ctx, WINBINDD_GETPWNAM,
1560                                       &request, &response, user);
1561
1562         switch (ret) {
1563                 case PAM_USER_UNKNOWN:
1564                         return 1;
1565                 case PAM_SUCCESS:
1566                         return 0;
1567                 default:
1568                         break;
1569         }
1570         return -1;
1571 }
1572
1573 static char *_pam_delete(register char *xx)
1574 {
1575         _pam_overwrite(xx);
1576         _pam_drop(xx);
1577         return NULL;
1578 }
1579
1580 /*
1581  * obtain a password from the user
1582  */
1583
1584 static int _winbind_read_password(struct pwb_context *ctx,
1585                                   unsigned int ctrl,
1586                                   const char *comment,
1587                                   const char *prompt1,
1588                                   const char *prompt2,
1589                                   const char **pass)
1590 {
1591         int authtok_flag;
1592         int retval;
1593         const char *item;
1594         char *token;
1595
1596         _pam_log(ctx, LOG_DEBUG, "getting password (0x%08x)", ctrl);
1597
1598         /*
1599          * make sure nothing inappropriate gets returned
1600          */
1601
1602         *pass = token = NULL;
1603
1604         /*
1605          * which authentication token are we getting?
1606          */
1607
1608         if (on(WINBIND__OLD_PASSWORD, ctrl)) {
1609                 authtok_flag = PAM_OLDAUTHTOK;
1610         } else {
1611                 authtok_flag = PAM_AUTHTOK;
1612         }
1613
1614         /*
1615          * should we obtain the password from a PAM item ?
1616          */
1617
1618         if (on(WINBIND_TRY_FIRST_PASS_ARG, ctrl) ||
1619             on(WINBIND_USE_FIRST_PASS_ARG, ctrl)) {
1620                 retval = _pam_get_item(ctx->pamh, authtok_flag, &item);
1621                 if (retval != PAM_SUCCESS) {
1622                         /* very strange. */
1623                         _pam_log(ctx, LOG_ALERT,
1624                                  "pam_get_item returned error "
1625                                  "to unix-read-password");
1626                         return retval;
1627                 } else if (item != NULL) {      /* we have a password! */
1628                         *pass = item;
1629                         item = NULL;
1630                         _pam_log(ctx, LOG_DEBUG,
1631                                  "pam_get_item returned a password");
1632                         return PAM_SUCCESS;
1633                 } else if (on(WINBIND_USE_FIRST_PASS_ARG, ctrl)) {
1634                         return PAM_AUTHTOK_RECOVER_ERR; /* didn't work */
1635                 } else if (on(WINBIND_USE_AUTHTOK_ARG, ctrl)
1636                            && off(WINBIND__OLD_PASSWORD, ctrl)) {
1637                         return PAM_AUTHTOK_RECOVER_ERR;
1638                 }
1639         }
1640         /*
1641          * getting here implies we will have to get the password from the
1642          * user directly.
1643          */
1644
1645         {
1646                 struct pam_message msg[3], *pmsg[3];
1647                 struct pam_response *resp;
1648                 int i, replies;
1649
1650                 /* prepare to converse */
1651
1652                 if (comment != NULL && off(ctrl, WINBIND_SILENT)) {
1653                         pmsg[0] = &msg[0];
1654                         msg[0].msg_style = PAM_TEXT_INFO;
1655                         msg[0].msg = discard_const_p(char, comment);
1656                         i = 1;
1657                 } else {
1658                         i = 0;
1659                 }
1660
1661                 pmsg[i] = &msg[i];
1662                 msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
1663                 msg[i++].msg = discard_const_p(char, prompt1);
1664                 replies = 1;
1665
1666                 if (prompt2 != NULL) {
1667                         pmsg[i] = &msg[i];
1668                         msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
1669                         msg[i++].msg = discard_const_p(char, prompt2);
1670                         ++replies;
1671                 }
1672                 /* so call the conversation expecting i responses */
1673                 resp = NULL;
1674                 retval = converse(ctx->pamh, i, pmsg, &resp);
1675                 if (resp == NULL) {
1676                         if (retval == PAM_SUCCESS) {
1677                                 retval = PAM_AUTHTOK_RECOVER_ERR;
1678                         }
1679                         goto done;
1680                 }
1681                 if (retval != PAM_SUCCESS) {
1682                         _pam_drop_reply(resp, i);
1683                         goto done;
1684                 }
1685
1686                 /* interpret the response */
1687
1688                 token = x_strdup(resp[i - replies].resp);
1689                 if (!token) {
1690                         _pam_log(ctx, LOG_NOTICE,
1691                                  "could not recover "
1692                                  "authentication token");
1693                         retval = PAM_AUTHTOK_RECOVER_ERR;
1694                         goto done;
1695                 }
1696
1697                 if (replies == 2) {
1698                         /* verify that password entered correctly */
1699                         if (!resp[i - 1].resp ||
1700                             strcmp(token, resp[i - 1].resp)) {
1701                                 _pam_delete(token);     /* mistyped */
1702                                 retval = PAM_AUTHTOK_RECOVER_ERR;
1703                                 _make_remark(ctx, PAM_ERROR_MSG,
1704                                              MISTYPED_PASS);
1705                         }
1706                 }
1707
1708                 /*
1709                  * tidy up the conversation (resp_retcode) is ignored
1710                  * -- what is it for anyway? AGM
1711                  */
1712                 _pam_drop_reply(resp, i);
1713         }
1714
1715  done:
1716         if (retval != PAM_SUCCESS) {
1717                 _pam_log_debug(ctx, LOG_DEBUG,
1718                                "unable to obtain a password");
1719                 return retval;
1720         }
1721         /* 'token' is the entered password */
1722
1723         /* we store this password as an item */
1724
1725         retval = pam_set_item(ctx->pamh, authtok_flag, token);
1726         _pam_delete(token);     /* clean it up */
1727         if (retval != PAM_SUCCESS ||
1728             (retval = _pam_get_item(ctx->pamh, authtok_flag, &item)) != PAM_SUCCESS) {
1729
1730                 _pam_log(ctx, LOG_CRIT, "error manipulating password");
1731                 return retval;
1732
1733         }
1734
1735         *pass = item;
1736         item = NULL;            /* break link to password */
1737
1738         return PAM_SUCCESS;
1739 }
1740
1741 static const char *get_conf_item_string(struct pwb_context *ctx,
1742                                         const char *item,
1743                                         int config_flag)
1744 {
1745         int i = 0;
1746         const char *parm_opt = NULL;
1747
1748         if (!(ctx->ctrl & config_flag)) {
1749                 goto out;
1750         }
1751
1752         /* let the pam opt take precedence over the pam_winbind.conf option */
1753         for (i=0; i<ctx->argc; i++) {
1754
1755                 if ((strncmp(ctx->argv[i], item, strlen(item)) == 0)) {
1756                         char *p;
1757
1758                         if ((p = strchr(ctx->argv[i], '=')) == NULL) {
1759                                 _pam_log(ctx, LOG_INFO,
1760                                          "no \"=\" delimiter for \"%s\" found\n",
1761                                          item);
1762                                 goto out;
1763                         }
1764                         _pam_log_debug(ctx, LOG_INFO,
1765                                        "PAM config: %s '%s'\n", item, p+1);
1766                         return p + 1;
1767                 }
1768         }
1769
1770         if (ctx->dict) {
1771                 char *key = NULL;
1772
1773                 if (!asprintf(&key, "global:%s", item)) {
1774                         goto out;
1775                 }
1776
1777                 parm_opt = iniparser_getstr(ctx->dict, key);
1778                 SAFE_FREE(key);
1779
1780                 _pam_log_debug(ctx, LOG_INFO, "CONFIG file: %s '%s'\n",
1781                                item, parm_opt);
1782         }
1783 out:
1784         return parm_opt;
1785 }
1786
1787 static int get_config_item_int(struct pwb_context *ctx,
1788                                const char *item,
1789                                int config_flag)
1790 {
1791         int i, parm_opt = -1;
1792
1793         if (!(ctx->ctrl & config_flag)) {
1794                 goto out;
1795         }
1796
1797         /* let the pam opt take precedence over the pam_winbind.conf option */
1798         for (i = 0; i < ctx->argc; i++) {
1799
1800                 if ((strncmp(ctx->argv[i], item, strlen(item)) == 0)) {
1801                         char *p;
1802
1803                         if ((p = strchr(ctx->argv[i], '=')) == NULL) {
1804                                 _pam_log(ctx, LOG_INFO,
1805                                          "no \"=\" delimiter for \"%s\" found\n",
1806                                          item);
1807                                 goto out;
1808                         }
1809                         parm_opt = atoi(p + 1);
1810                         _pam_log_debug(ctx, LOG_INFO,
1811                                        "PAM config: %s '%d'\n",
1812                                        item, parm_opt);
1813                         return parm_opt;
1814                 }
1815         }
1816
1817         if (ctx->dict) {
1818                 char *key = NULL;
1819
1820                 if (!asprintf(&key, "global:%s", item)) {
1821                         goto out;
1822                 }
1823
1824                 parm_opt = iniparser_getint(ctx->dict, key, -1);
1825                 SAFE_FREE(key);
1826
1827                 _pam_log_debug(ctx, LOG_INFO,
1828                                "CONFIG file: %s '%d'\n",
1829                                item, parm_opt);
1830         }
1831 out:
1832         return parm_opt;
1833 }
1834
1835 static const char *get_krb5_cc_type_from_config(struct pwb_context *ctx)
1836 {
1837         return get_conf_item_string(ctx, "krb5_ccache_type",
1838                                     WINBIND_KRB5_CCACHE_TYPE);
1839 }
1840
1841 static const char *get_member_from_config(struct pwb_context *ctx)
1842 {
1843         const char *ret = NULL;
1844         ret = get_conf_item_string(ctx, "require_membership_of",
1845                                    WINBIND_REQUIRED_MEMBERSHIP);
1846         if (ret) {
1847                 return ret;
1848         }
1849         return get_conf_item_string(ctx, "require-membership-of",
1850                                     WINBIND_REQUIRED_MEMBERSHIP);
1851 }
1852
1853 static int get_warn_pwd_expire_from_config(struct pwb_context *ctx)
1854 {
1855         int ret;
1856         ret = get_config_item_int(ctx, "warn_pwd_expire",
1857                                   WINBIND_WARN_PWD_EXPIRE);
1858         /* no or broken setting */
1859         if (ret <= 0) {
1860                 return DEFAULT_DAYS_TO_WARN_BEFORE_PWD_EXPIRES;
1861         }
1862         return ret;
1863 }
1864
1865 /**
1866  * Retrieve the winbind separator.
1867  *
1868  * @param pamh PAM handle
1869  * @param ctrl PAM winbind options.
1870  *
1871  * @return string separator character. NULL on failure.
1872  */
1873
1874 static char winbind_get_separator(struct pwb_context *ctx)
1875 {
1876         struct winbindd_request request;
1877         struct winbindd_response response;
1878
1879         ZERO_STRUCT(request);
1880         ZERO_STRUCT(response);
1881
1882         if (pam_winbind_request_log(ctx, WINBINDD_INFO,
1883                                     &request, &response, NULL)) {
1884                 return '\0';
1885         }
1886
1887         return response.data.info.winbind_separator;
1888 }
1889
1890 /**
1891  * Convert a upn to a name.
1892  *
1893  * @param pamh PAM handle
1894  * @param ctrl PAM winbind options.
1895  * @param upn  USer UPN to be trabslated.
1896  *
1897  * @return converted name. NULL pointer on failure. Caller needs to free.
1898  */
1899
1900 static char* winbind_upn_to_username(struct pwb_context *ctx,
1901                                      const char *upn)
1902 {
1903         struct winbindd_request req;
1904         struct winbindd_response resp;
1905         int retval;
1906         char *account_name;
1907         int account_name_len;
1908         char sep;
1909
1910         /* This cannot work when the winbind separator = @ */
1911
1912         sep = winbind_get_separator(ctx);
1913         if (!sep || sep == '@') {
1914                 return NULL;
1915         }
1916
1917         /* Convert the UPN to a SID */
1918
1919         ZERO_STRUCT(req);
1920         ZERO_STRUCT(resp);
1921
1922         strncpy(req.data.name.dom_name, "",
1923                 sizeof(req.data.name.dom_name) - 1);
1924         strncpy(req.data.name.name, upn,
1925                 sizeof(req.data.name.name) - 1);
1926         retval = pam_winbind_request_log(ctx, WINBINDD_LOOKUPNAME,
1927                                          &req, &resp, upn);
1928         if (retval != PAM_SUCCESS) {
1929                 return NULL;
1930         }
1931
1932         /* Convert the the SID back to the sAMAccountName */
1933
1934         ZERO_STRUCT(req);
1935         strncpy(req.data.sid, resp.data.sid.sid, sizeof(req.data.sid)-1);
1936         ZERO_STRUCT(resp);
1937         retval =  pam_winbind_request_log(ctx, WINBINDD_LOOKUPSID,
1938                                           &req, &resp, upn);
1939         if (retval != PAM_SUCCESS) {
1940                 return NULL;
1941         }
1942
1943         account_name_len = asprintf(&account_name, "%s\\%s",
1944                                     resp.data.name.dom_name,
1945                                     resp.data.name.name);
1946
1947         return account_name;
1948 }
1949
1950 PAM_EXTERN
1951 int pam_sm_authenticate(pam_handle_t *pamh, int flags,
1952                         int argc, const char **argv)
1953 {
1954         const char *username;
1955         const char *password;
1956         const char *member = NULL;
1957         const char *cctype = NULL;
1958         int warn_pwd_expire;
1959         int retval = PAM_AUTH_ERR;
1960         char *username_ret = NULL;
1961         char *new_authtok_required = NULL;
1962         char *real_username = NULL;
1963         struct pwb_context *ctx = NULL;
1964
1965         retval = _pam_winbind_init_context(pamh, flags, argc, argv, &ctx);
1966         if (retval) {
1967                 goto out;
1968         }
1969
1970         _PAM_LOG_FUNCTION_ENTER("pam_sm_authenticate", ctx);
1971
1972         /* Get the username */
1973         retval = pam_get_user(pamh, &username, NULL);
1974         if ((retval != PAM_SUCCESS) || (!username)) {
1975                 _pam_log_debug(ctx, LOG_DEBUG,
1976                                "can not get the username");
1977                 retval = PAM_SERVICE_ERR;
1978                 goto out;
1979         }
1980
1981
1982 #if defined(AIX)
1983         /* Decode the user name since AIX does not support logn user
1984            names by default.  The name is encoded as _#uid.  */
1985
1986         if (username[0] == '_') {
1987                 uid_t id = atoi(&username[1]);
1988                 struct passwd *pw = NULL;
1989
1990                 if ((id!=0) && ((pw = getpwuid(id)) != NULL)) {
1991                         real_username = strdup(pw->pw_name);
1992                 }
1993         }
1994 #endif
1995
1996         if (!real_username) {
1997                 /* Just making a copy of the username we got from PAM */
1998                 if ((real_username = strdup(username)) == NULL) {
1999                         _pam_log_debug(ctx, LOG_DEBUG,
2000                                        "memory allocation failure when copying "
2001                                        "username");
2002                         retval = PAM_SERVICE_ERR;
2003                         goto out;
2004                 }
2005         }
2006
2007         /* Maybe this was a UPN */
2008
2009         if (strchr(real_username, '@') != NULL) {
2010                 char *samaccountname = NULL;
2011
2012                 samaccountname = winbind_upn_to_username(ctx,
2013                                                          real_username);
2014                 if (samaccountname) {
2015                         free(real_username);
2016                         real_username = samaccountname;
2017                 }
2018         }
2019
2020         retval = _winbind_read_password(ctx, ctx->ctrl, NULL,
2021                                         "Password: ", NULL,
2022                                         &password);
2023
2024         if (retval != PAM_SUCCESS) {
2025                 _pam_log(ctx, LOG_ERR,
2026                          "Could not retrieve user's password");
2027                 retval = PAM_AUTHTOK_ERR;
2028                 goto out;
2029         }
2030
2031         /* Let's not give too much away in the log file */
2032
2033 #ifdef DEBUG_PASSWORD
2034         _pam_log_debug(ctx, LOG_INFO,
2035                        "Verify user '%s' with password '%s'",
2036                        real_username, password);
2037 #else
2038         _pam_log_debug(ctx, LOG_INFO,
2039                        "Verify user '%s'", real_username);
2040 #endif
2041
2042         member = get_member_from_config(ctx);
2043         cctype = get_krb5_cc_type_from_config(ctx);
2044         warn_pwd_expire = get_warn_pwd_expire_from_config(ctx);
2045
2046         /* Now use the username to look up password */
2047         retval = winbind_auth_request(ctx, real_username, password,
2048                                       member, cctype, warn_pwd_expire, NULL,
2049                                       NULL, &username_ret);
2050
2051         if (retval == PAM_NEW_AUTHTOK_REQD ||
2052             retval == PAM_AUTHTOK_EXPIRED) {
2053
2054                 char *new_authtok_required_during_auth = NULL;
2055
2056                 if (!asprintf(&new_authtok_required, "%d", retval)) {
2057                         retval = PAM_BUF_ERR;
2058                         goto out;
2059                 }
2060
2061                 pam_set_data(pamh, PAM_WINBIND_NEW_AUTHTOK_REQD,
2062                              new_authtok_required,
2063                              _pam_winbind_cleanup_func);
2064
2065                 retval = PAM_SUCCESS;
2066
2067                 if (!asprintf(&new_authtok_required_during_auth, "%d", true)) {
2068                         retval = PAM_BUF_ERR;
2069                         goto out;
2070                 }
2071
2072                 pam_set_data(pamh, PAM_WINBIND_NEW_AUTHTOK_REQD_DURING_AUTH,
2073                              new_authtok_required_during_auth,
2074                              _pam_winbind_cleanup_func);
2075
2076                 goto out;
2077         }
2078
2079 out:
2080         if (username_ret) {
2081                 pam_set_item (pamh, PAM_USER, username_ret);
2082                 _pam_log_debug(ctx, LOG_INFO,
2083                                "Returned user was '%s'", username_ret);
2084                 free(username_ret);
2085         }
2086
2087         if (real_username) {
2088                 free(real_username);
2089         }
2090
2091         if (!new_authtok_required) {
2092                 pam_set_data(pamh, PAM_WINBIND_NEW_AUTHTOK_REQD, NULL, NULL);
2093         }
2094
2095         if (retval != PAM_SUCCESS) {
2096                 _pam_free_data_info3(pamh);
2097         }
2098
2099         _PAM_LOG_FUNCTION_LEAVE("pam_sm_authenticate", ctx, retval);
2100
2101         _pam_winbind_free_context(ctx);
2102
2103         return retval;
2104 }
2105
2106 PAM_EXTERN
2107 int pam_sm_setcred(pam_handle_t *pamh, int flags,
2108                    int argc, const char **argv)
2109 {
2110         int ret = PAM_SYSTEM_ERR;
2111         struct pwb_context *ctx = NULL;
2112
2113         ret = _pam_winbind_init_context(pamh, flags, argc, argv, &ctx);
2114         if (ret) {
2115                 goto out;
2116         }
2117
2118         _PAM_LOG_FUNCTION_ENTER("pam_sm_setcred", ctx);
2119
2120         switch (flags & ~PAM_SILENT) {
2121
2122                 case PAM_DELETE_CRED:
2123                         ret = pam_sm_close_session(pamh, flags, argc, argv);
2124                         break;
2125                 case PAM_REFRESH_CRED:
2126                         _pam_log_debug(ctx, LOG_WARNING,
2127                                        "PAM_REFRESH_CRED not implemented");
2128                         ret = PAM_SUCCESS;
2129                         break;
2130                 case PAM_REINITIALIZE_CRED:
2131                         _pam_log_debug(ctx, LOG_WARNING,
2132                                        "PAM_REINITIALIZE_CRED not implemented");
2133                         ret = PAM_SUCCESS;
2134                         break;
2135                 case PAM_ESTABLISH_CRED:
2136                         _pam_log_debug(ctx, LOG_WARNING,
2137                                        "PAM_ESTABLISH_CRED not implemented");
2138                         ret = PAM_SUCCESS;
2139                         break;
2140                 default:
2141                         ret = PAM_SYSTEM_ERR;
2142                         break;
2143         }
2144
2145  out:
2146
2147         _PAM_LOG_FUNCTION_LEAVE("pam_sm_setcred", ctx, ret);
2148
2149         _pam_winbind_free_context(ctx);
2150
2151         return ret;
2152 }
2153
2154 /*
2155  * Account management. We want to verify that the account exists
2156  * before returning PAM_SUCCESS
2157  */
2158 PAM_EXTERN
2159 int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
2160                    int argc, const char **argv)
2161 {
2162         const char *username;
2163         int ret = PAM_USER_UNKNOWN;
2164         void *tmp = NULL;
2165         struct pwb_context *ctx = NULL;
2166
2167         ret = _pam_winbind_init_context(pamh, flags, argc, argv, &ctx);
2168         if (ret) {
2169                 goto out;
2170         }
2171
2172         _PAM_LOG_FUNCTION_ENTER("pam_sm_acct_mgmt", ctx);
2173
2174
2175         /* Get the username */
2176         ret = pam_get_user(pamh, &username, NULL);
2177         if ((ret != PAM_SUCCESS) || (!username)) {
2178                 _pam_log_debug(ctx, LOG_DEBUG,
2179                                "can not get the username");
2180                 ret = PAM_SERVICE_ERR;
2181                 goto out;
2182         }
2183
2184         /* Verify the username */
2185         ret = valid_user(ctx, username);
2186         switch (ret) {
2187         case -1:
2188                 /* some sort of system error. The log was already printed */
2189                 ret = PAM_SERVICE_ERR;
2190                 goto out;
2191         case 1:
2192                 /* the user does not exist */
2193                 _pam_log_debug(ctx, LOG_NOTICE, "user '%s' not found",
2194                                username);
2195                 if (ctx->ctrl & WINBIND_UNKNOWN_OK_ARG) {
2196                         ret = PAM_IGNORE;
2197                         goto out;
2198                 }
2199                 ret = PAM_USER_UNKNOWN;
2200                 goto out;
2201         case 0:
2202                 pam_get_data(pamh, PAM_WINBIND_NEW_AUTHTOK_REQD,
2203                              (const void **)&tmp);
2204                 if (tmp != NULL) {
2205                         ret = atoi((const char *)tmp);
2206                         switch (ret) {
2207                         case PAM_AUTHTOK_EXPIRED:
2208                                 /* fall through, since new token is required in this case */
2209                         case PAM_NEW_AUTHTOK_REQD:
2210                                 _pam_log(ctx, LOG_WARNING,
2211                                          "pam_sm_acct_mgmt success but %s is set",
2212                                          PAM_WINBIND_NEW_AUTHTOK_REQD);
2213                                 _pam_log(ctx, LOG_NOTICE,
2214                                          "user '%s' needs new password",
2215                                          username);
2216                                 /* PAM_AUTHTOKEN_REQD does not exist, but is documented in the manpage */
2217                                 ret = PAM_NEW_AUTHTOK_REQD;
2218                                 goto out;
2219                         default:
2220                                 _pam_log(ctx, LOG_WARNING,
2221                                          "pam_sm_acct_mgmt success");
2222                                 _pam_log(ctx, LOG_NOTICE,
2223                                          "user '%s' granted access", username);
2224                                 ret = PAM_SUCCESS;
2225                                 goto out;
2226                         }
2227                 }
2228
2229                 /* Otherwise, the authentication looked good */
2230                 _pam_log(ctx, LOG_NOTICE,
2231                          "user '%s' granted access", username);
2232                 ret = PAM_SUCCESS;
2233                 goto out;
2234         default:
2235                 /* we don't know anything about this return value */
2236                 _pam_log(ctx, LOG_ERR,
2237                          "internal module error (ret = %d, user = '%s')",
2238                          ret, username);
2239                 ret = PAM_SERVICE_ERR;
2240                 goto out;
2241         }
2242
2243         /* should not be reached */
2244         ret = PAM_IGNORE;
2245
2246  out:
2247
2248         _PAM_LOG_FUNCTION_LEAVE("pam_sm_acct_mgmt", ctx, ret);
2249
2250         _pam_winbind_free_context(ctx);
2251
2252         return ret;
2253 }
2254
2255 PAM_EXTERN
2256 int pam_sm_open_session(pam_handle_t *pamh, int flags,
2257                         int argc, const char **argv)
2258 {
2259         int ret = PAM_SYSTEM_ERR;
2260         struct pwb_context *ctx = NULL;
2261
2262         ret = _pam_winbind_init_context(pamh, flags, argc, argv, &ctx);
2263         if (ret) {
2264                 goto out;
2265         }
2266
2267         _PAM_LOG_FUNCTION_ENTER("pam_sm_open_session", ctx);
2268
2269         ret = PAM_SUCCESS;
2270
2271  out:
2272         _PAM_LOG_FUNCTION_LEAVE("pam_sm_open_session", ctx, ret);
2273
2274         _pam_winbind_free_context(ctx);
2275
2276         return ret;
2277 }
2278
2279 PAM_EXTERN
2280 int pam_sm_close_session(pam_handle_t *pamh, int flags,
2281                          int argc, const char **argv)
2282 {
2283         int retval = PAM_SUCCESS;
2284         struct pwb_context *ctx = NULL;
2285
2286         retval = _pam_winbind_init_context(pamh, flags, argc, argv, &ctx);
2287         if (retval) {
2288                 goto out;
2289         }
2290
2291         _PAM_LOG_FUNCTION_ENTER("pam_sm_close_session", ctx);
2292
2293         if (!(flags & PAM_DELETE_CRED)) {
2294                 retval = PAM_SUCCESS;
2295                 goto out;
2296         }
2297
2298         if (ctx->ctrl & WINBIND_KRB5_AUTH) {
2299
2300                 /* destroy the ccache here */
2301                 struct winbindd_request request;
2302                 struct winbindd_response response;
2303                 const char *user;
2304                 const char *ccname = NULL;
2305                 struct passwd *pwd = NULL;
2306
2307                 ZERO_STRUCT(request);
2308                 ZERO_STRUCT(response);
2309
2310                 retval = pam_get_user(pamh, &user, "Username: ");
2311                 if (retval) {
2312                         _pam_log(ctx, LOG_ERR,
2313                                  "could not identify user");
2314                         goto out;
2315                 }
2316
2317                 if (user == NULL) {
2318                         _pam_log(ctx, LOG_ERR,
2319                                  "username was NULL!");
2320                         retval = PAM_USER_UNKNOWN;
2321                         goto out;
2322                 }
2323
2324                 _pam_log_debug(ctx, LOG_DEBUG,
2325                                "username [%s] obtained", user);
2326
2327                 ccname = pam_getenv(pamh, "KRB5CCNAME");
2328                 if (ccname == NULL) {
2329                         _pam_log_debug(ctx, LOG_DEBUG,
2330                                        "user has no KRB5CCNAME environment");
2331                 }
2332
2333                 strncpy(request.data.logoff.user, user,
2334                         sizeof(request.data.logoff.user) - 1);
2335
2336                 if (ccname) {
2337                         strncpy(request.data.logoff.krb5ccname, ccname,
2338                                 sizeof(request.data.logoff.krb5ccname) - 1);
2339                 }
2340
2341                 pwd = getpwnam(user);
2342                 if (pwd == NULL) {
2343                         retval = PAM_USER_UNKNOWN;
2344                         goto out;
2345                 }
2346                 request.data.logoff.uid = pwd->pw_uid;
2347
2348                 request.flags = WBFLAG_PAM_KRB5 |
2349                                 WBFLAG_PAM_CONTACT_TRUSTDOM;
2350
2351                 retval = pam_winbind_request_log(ctx,
2352                                                  WINBINDD_PAM_LOGOFF,
2353                                                  &request, &response, user);
2354         }
2355
2356 out:
2357
2358         _PAM_LOG_FUNCTION_LEAVE("pam_sm_close_session", ctx, retval);
2359
2360         _pam_winbind_free_context(ctx);
2361
2362         return retval;
2363 }
2364
2365 /**
2366  * evaluate whether we need to re-authenticate with kerberos after a
2367  * password change
2368  *
2369  * @param pamh PAM handle
2370  * @param ctrl PAM winbind options.
2371  * @param user The username
2372  *
2373  * @return boolean Returns true if required, false if not.
2374  */
2375
2376 static bool _pam_require_krb5_auth_after_chauthtok(struct pwb_context *ctx,
2377                                                    const char *user)
2378 {
2379
2380         /* Make sure that we only do this if a) the chauthtok got initiated
2381          * during a logon attempt (authenticate->acct_mgmt->chauthtok) b) any
2382          * later password change via the "passwd" command if done by the user
2383          * itself */
2384
2385         char *new_authtok_reqd_during_auth = NULL;
2386         struct passwd *pwd = NULL;
2387
2388         if (!(ctx->ctrl & WINBIND_KRB5_AUTH)) {
2389                 return false;
2390         }
2391
2392         _pam_get_data(ctx->pamh, PAM_WINBIND_NEW_AUTHTOK_REQD_DURING_AUTH,
2393                       &new_authtok_reqd_during_auth);
2394         pam_set_data(ctx->pamh, PAM_WINBIND_NEW_AUTHTOK_REQD_DURING_AUTH,
2395                      NULL, NULL);
2396
2397         if (new_authtok_reqd_during_auth) {
2398                 return true;
2399         }
2400
2401         pwd = getpwnam(user);
2402         if (!pwd) {
2403                 return false;
2404         }
2405
2406         if (getuid() == pwd->pw_uid) {
2407                 return true;
2408         }
2409
2410         return false;
2411 }
2412
2413
2414 PAM_EXTERN
2415 int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
2416                      int argc, const char **argv)
2417 {
2418         unsigned int lctrl;
2419         int ret;
2420
2421         /* <DO NOT free() THESE> */
2422         const char *user;
2423         char *pass_old, *pass_new;
2424         /* </DO NOT free() THESE> */
2425
2426         char *Announce;
2427
2428         int retry = 0;
2429         char *username_ret = NULL;
2430         struct winbindd_response response;
2431         struct pwb_context *ctx = NULL;
2432
2433         ZERO_STRUCT(response);
2434
2435         ret = _pam_winbind_init_context(pamh, flags, argc, argv, &ctx);
2436         if (ret) {
2437                 goto out;
2438         }
2439
2440         _PAM_LOG_FUNCTION_ENTER("pam_sm_chauthtok", ctx);
2441
2442         /* clearing offline bit for the auth in the password change */
2443         ctx->ctrl &= ~WINBIND_CACHED_LOGIN;
2444
2445         /*
2446          * First get the name of a user
2447          */
2448         ret = pam_get_user(pamh, &user, "Username: ");
2449         if (ret) {
2450                 _pam_log(ctx, LOG_ERR,
2451                          "password - could not identify user");
2452                 goto out;
2453         }
2454
2455         if (user == NULL) {
2456                 _pam_log(ctx, LOG_ERR, "username was NULL!");
2457                 ret = PAM_USER_UNKNOWN;
2458                 goto out;
2459         }
2460
2461         _pam_log_debug(ctx, LOG_DEBUG, "username [%s] obtained", user);
2462
2463         /* check if this is really a user in winbindd, not only in NSS */
2464         ret = valid_user(ctx, user);
2465         switch (ret) {
2466                 case 1:
2467                         ret = PAM_USER_UNKNOWN;
2468                         goto out;
2469                 case -1:
2470                         ret = PAM_SYSTEM_ERR;
2471                         goto out;
2472                 default:
2473                         break;
2474         }
2475
2476         /*
2477          * obtain and verify the current password (OLDAUTHTOK) for
2478          * the user.
2479          */
2480
2481         if (flags & PAM_PRELIM_CHECK) {
2482                 time_t pwdlastset_prelim = 0;
2483
2484                 /* instruct user what is happening */
2485 #define greeting "Changing password for "
2486                 Announce = (char *) malloc(sizeof(greeting) + strlen(user));
2487                 if (Announce == NULL) {
2488                         _pam_log(ctx, LOG_CRIT,
2489                                  "password - out of memory");
2490                         ret = PAM_BUF_ERR;
2491                         goto out;
2492                 }
2493                 (void) strcpy(Announce, greeting);
2494                 (void) strcpy(Announce + sizeof(greeting) - 1, user);
2495 #undef greeting
2496
2497                 lctrl = ctx->ctrl | WINBIND__OLD_PASSWORD;
2498                 ret = _winbind_read_password(ctx, lctrl,
2499                                                 Announce,
2500                                                 "(current) NT password: ",
2501                                                 NULL,
2502                                                 (const char **) &pass_old);
2503                 if (ret != PAM_SUCCESS) {
2504                         _pam_log(ctx, LOG_NOTICE,
2505                                  "password - (old) token not obtained");
2506                         goto out;
2507                 }
2508
2509                 /* verify that this is the password for this user */
2510
2511                 ret = winbind_auth_request(ctx, user, pass_old,
2512                                            NULL, NULL, 0, &response,
2513                                            &pwdlastset_prelim, NULL);
2514
2515                 if (ret != PAM_ACCT_EXPIRED &&
2516                     ret != PAM_AUTHTOK_EXPIRED &&
2517                     ret != PAM_NEW_AUTHTOK_REQD &&
2518                     ret != PAM_SUCCESS) {
2519                         pass_old = NULL;
2520                         goto out;
2521                 }
2522
2523                 pam_set_data(pamh, PAM_WINBIND_PWD_LAST_SET,
2524                              (void *)pwdlastset_prelim, NULL);
2525
2526                 ret = pam_set_item(pamh, PAM_OLDAUTHTOK,
2527                                    (const void *) pass_old);
2528                 pass_old = NULL;
2529                 if (ret != PAM_SUCCESS) {
2530                         _pam_log(ctx, LOG_CRIT,
2531                                  "failed to set PAM_OLDAUTHTOK");
2532                 }
2533         } else if (flags & PAM_UPDATE_AUTHTOK) {
2534
2535                 time_t pwdlastset_update = 0;
2536
2537                 /*
2538                  * obtain the proposed password
2539                  */
2540
2541                 /*
2542                  * get the old token back.
2543                  */
2544
2545                 ret = _pam_get_item(pamh, PAM_OLDAUTHTOK, &pass_old);
2546
2547                 if (ret != PAM_SUCCESS) {
2548                         _pam_log(ctx, LOG_NOTICE,
2549                                  "user not authenticated");
2550                         goto out;
2551                 }
2552
2553                 lctrl = ctx->ctrl & ~WINBIND_TRY_FIRST_PASS_ARG;
2554
2555                 if (on(WINBIND_USE_AUTHTOK_ARG, lctrl)) {
2556                         lctrl |= WINBIND_USE_FIRST_PASS_ARG;
2557                 }
2558                 retry = 0;
2559                 ret = PAM_AUTHTOK_ERR;
2560                 while ((ret != PAM_SUCCESS) && (retry++ < MAX_PASSWD_TRIES)) {
2561                         /*
2562                          * use_authtok is to force the use of a previously entered
2563                          * password -- needed for pluggable password strength checking
2564                          */
2565
2566                         ret = _winbind_read_password(ctx, lctrl,
2567                                                      NULL,
2568                                                      "Enter new NT password: ",
2569                                                      "Retype new NT password: ",
2570                                                      (const char **)&pass_new);
2571
2572                         if (ret != PAM_SUCCESS) {
2573                                 _pam_log_debug(ctx, LOG_ALERT,
2574                                                "password - "
2575                                                "new password not obtained");
2576                                 pass_old = NULL;/* tidy up */
2577                                 goto out;
2578                         }
2579
2580                         /*
2581                          * At this point we know who the user is and what they
2582                          * propose as their new password. Verify that the new
2583                          * password is acceptable.
2584                          */
2585
2586                         if (pass_new[0] == '\0') {/* "\0" password = NULL */
2587                                 pass_new = NULL;
2588                         }
2589                 }
2590
2591                 /*
2592                  * By reaching here we have approved the passwords and must now
2593                  * rebuild the password database file.
2594                  */
2595                 _pam_get_data(pamh, PAM_WINBIND_PWD_LAST_SET,
2596                               &pwdlastset_update);
2597
2598                 ret = winbind_chauthtok_request(ctx, user, pass_old,
2599                                                 pass_new, pwdlastset_update);
2600                 if (ret) {
2601                         _pam_overwrite(pass_new);
2602                         _pam_overwrite(pass_old);
2603                         pass_old = pass_new = NULL;
2604                         goto out;
2605                 }
2606
2607                 if (_pam_require_krb5_auth_after_chauthtok(ctx, user)) {
2608
2609                         const char *member = NULL;
2610                         const char *cctype = NULL;
2611                         int warn_pwd_expire;
2612
2613                         member = get_member_from_config(ctx);
2614                         cctype = get_krb5_cc_type_from_config(ctx);
2615                         warn_pwd_expire = get_warn_pwd_expire_from_config(ctx);
2616
2617                         ret = winbind_auth_request(ctx, user, pass_new,
2618                                                    member, cctype, 0, &response,
2619                                                    NULL, &username_ret);
2620                         _pam_overwrite(pass_new);
2621                         _pam_overwrite(pass_old);
2622                         pass_old = pass_new = NULL;
2623
2624                         if (ret == PAM_SUCCESS) {
2625
2626                                 /* warn a user if the password is about to
2627                                  * expire soon */
2628                                 _pam_warn_password_expiry(ctx, &response,
2629                                                           warn_pwd_expire,
2630                                                           NULL);
2631
2632                                 /* set some info3 info for other modules in the
2633                                  * stack */
2634                                 _pam_set_data_info3(ctx, &response);
2635
2636                                 /* put krb5ccname into env */
2637                                 _pam_setup_krb5_env(ctx,
2638                                                     response.data.auth.krb5ccname);
2639
2640                                 if (username_ret) {
2641                                         pam_set_item(pamh, PAM_USER,
2642                                                      username_ret);
2643                                         _pam_log_debug(ctx, LOG_INFO,
2644                                                        "Returned user was '%s'",
2645                                                        username_ret);
2646                                         free(username_ret);
2647                                 }
2648                         }
2649
2650                         goto out;
2651                 }
2652         } else {
2653                 ret = PAM_SERVICE_ERR;
2654         }
2655
2656 out:
2657
2658         /* Deal with offline errors. */
2659         PAM_WB_REMARK_CHECK_RESPONSE(ctx, response,
2660                                      "NT_STATUS_NO_LOGON_SERVERS");
2661         PAM_WB_REMARK_CHECK_RESPONSE(ctx, response,
2662                                      "NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND");
2663         PAM_WB_REMARK_CHECK_RESPONSE(ctx, response,
2664                                      "NT_STATUS_ACCESS_DENIED");
2665
2666         _PAM_LOG_FUNCTION_LEAVE("pam_sm_chauthtok", ctx, ret);
2667
2668         _pam_winbind_free_context(ctx);
2669
2670         return ret;
2671 }
2672
2673 #ifdef PAM_STATIC
2674
2675 /* static module data */
2676
2677 struct pam_module _pam_winbind_modstruct = {
2678         MODULE_NAME,
2679         pam_sm_authenticate,
2680         pam_sm_setcred,
2681         pam_sm_acct_mgmt,
2682         pam_sm_open_session,
2683         pam_sm_close_session,
2684         pam_sm_chauthtok
2685 };
2686
2687 #endif
2688
2689 /*
2690  * Copyright (c) Andrew Tridgell  <tridge@samba.org>   2000
2691  * Copyright (c) Tim Potter       <tpot@samba.org>     2000
2692  * Copyright (c) Andrew Bartlettt <abartlet@samba.org> 2002
2693  * Copyright (c) Guenther Deschner <gd@samba.org>      2005-2007
2694  * Copyright (c) Jan Rêkorajski 1999.
2695  * Copyright (c) Andrew G. Morgan 1996-8.
2696  * Copyright (c) Alex O. Yuriev, 1996.
2697  * Copyright (c) Cristian Gafton 1996.
2698  * Copyright (C) Elliot Lee <sopwith@redhat.com> 1996, Red Hat Software.
2699  *
2700  * Redistribution and use in source and binary forms, with or without
2701  * modification, are permitted provided that the following conditions
2702  * are met:
2703  * 1. Redistributions of source code must retain the above copyright
2704  *    notice, and the entire permission notice in its entirety,
2705  *    including the disclaimer of warranties.
2706  * 2. Redistributions in binary form must reproduce the above copyright
2707  *    notice, this list of conditions and the following disclaimer in the
2708  *    documentation and/or other materials provided with the distribution.
2709  * 3. The name of the author may not be used to endorse or promote
2710  *    products derived from this software without specific prior
2711  *    written permission.
2712  *
2713  * ALTERNATIVELY, this product may be distributed under the terms of
2714  * the GNU Public License, in which case the provisions of the GPL are
2715  * required INSTEAD OF the above restrictions.  (This clause is
2716  * necessary due to a potential bad interaction between the GPL and
2717  * the restrictions contained in a BSD-style copyright.)
2718  *
2719  * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED
2720  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2721  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
2722  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
2723  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
2724  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
2725  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2726  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
2727  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2728  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
2729  * OF THE POSSIBILITY OF SUCH DAMAGE.
2730  */