source: trunk/samba/source/nsswitch/pam_winbind.c @ 26

Last change on this file since 26 was 26, checked in by Paul Smedley, 14 years ago

Updated source to 3.0.25rc1

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