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