r16945: Sync trunk -> 3.0 for 3.0.24 code. Still need
[vlendec/samba-autobuild/.git] / source3 / nsswitch / pam_winbind.c
1 /* pam_winbind module
2
3    Copyright Andrew Tridgell <tridge@samba.org> 2000
4    Copyright Tim Potter <tpot@samba.org> 2000
5    Copyright Andrew Bartlett <abartlet@samba.org> 2002
6    Copyright Guenther Deschner <gd@samba.org> 2005-2006
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 /* data tokens */
16
17 #define MAX_PASSWD_TRIES        3
18
19 /* some syslogging */
20 static void _pam_log(int err, const char *format, ...)
21 {
22         va_list args;
23
24         va_start(args, format);
25         openlog(MODULE_NAME, LOG_CONS|LOG_PID, LOG_AUTH);
26         vsyslog(err, format, args);
27         va_end(args);
28         closelog();
29 }
30
31 static void _pam_log_debug(int ctrl, int err, const char *format, ...)
32 {
33         va_list args;
34
35         if (!(ctrl & WINBIND_DEBUG_ARG)) {
36                 return;
37         }
38
39         va_start(args, format);
40         openlog(MODULE_NAME, LOG_CONS|LOG_PID, LOG_AUTH);
41         vsyslog(err, format, args);
42         va_end(args);
43         closelog();
44 }
45
46 static int _pam_parse(int argc, const char **argv, dictionary **d)
47 {
48         int ctrl = 0;
49         const char *config_file = NULL;
50
51         if (d == NULL) {
52                 goto config_from_pam;
53         }
54
55         for (; argc-- > 0; ++argv) {
56                 if (!strncasecmp(*argv, "config", strlen("config"))) {
57                         ctrl |= WINBIND_CONFIG_FILE;
58                         config_file = argv[argc];
59                         break;
60                 }
61         }
62
63         if (config_file == NULL) {
64                 config_file = PAM_WINBIND_CONFIG_FILE;
65         }
66
67         *d = iniparser_load(CONST_DISCARD(char *, config_file));
68         if (*d == NULL) {
69                 goto config_from_pam;
70         }
71
72         if (iniparser_getboolean(*d, CONST_DISCARD(char *, "global:debug"), False)) {
73                 ctrl |= WINBIND_DEBUG_ARG;
74         }
75
76         if (iniparser_getboolean(*d, CONST_DISCARD(char *, "global:cached_login"), False)) {
77                 ctrl |= WINBIND_CACHED_LOGIN;
78         }
79
80         if (iniparser_getboolean(*d, CONST_DISCARD(char *, "global:krb5_auth"), False)) {
81                 ctrl |= WINBIND_KRB5_AUTH;
82         }
83
84         if (iniparser_getstr(*d, CONST_DISCARD(char *,"global:krb5_ccache_type")) != NULL) {
85                 ctrl |= WINBIND_KRB5_CCACHE_TYPE;
86         }
87         
88         if ((iniparser_getstr(*d, CONST_DISCARD(char *, "global:require-membership-of")) != NULL) ||
89             (iniparser_getstr(*d, CONST_DISCARD(char *, "global:require_membership_of")) != NULL)) {
90                 ctrl |= WINBIND_REQUIRED_MEMBERSHIP;
91         }
92
93 config_from_pam:
94         /* step through arguments */
95         for (; argc-- > 0; ++argv) {
96
97                 /* generic options */
98                 if (!strcmp(*argv,"debug"))
99                         ctrl |= WINBIND_DEBUG_ARG;
100                 else if (!strcasecmp(*argv, "use_authtok"))
101                         ctrl |= WINBIND_USE_AUTHTOK_ARG;
102                 else if (!strcasecmp(*argv, "use_first_pass"))
103                         ctrl |= WINBIND_USE_FIRST_PASS_ARG;
104                 else if (!strcasecmp(*argv, "try_first_pass"))
105                         ctrl |= WINBIND_TRY_FIRST_PASS_ARG;
106                 else if (!strcasecmp(*argv, "unknown_ok"))
107                         ctrl |= WINBIND_UNKNOWN_OK_ARG;
108                 else if (!strncasecmp(*argv, "require_membership_of", strlen("require_membership_of")))
109                         ctrl |= WINBIND_REQUIRED_MEMBERSHIP;
110                 else if (!strncasecmp(*argv, "require-membership-of", strlen("require-membership-of")))
111                         ctrl |= WINBIND_REQUIRED_MEMBERSHIP;
112                 else if (!strcasecmp(*argv, "krb5_auth"))
113                         ctrl |= WINBIND_KRB5_AUTH;
114                 else if (!strncasecmp(*argv, "krb5_ccache_type", strlen("krb5_ccache_type")))
115                         ctrl |= WINBIND_KRB5_CCACHE_TYPE;
116                 else if (!strcasecmp(*argv, "cached_login"))
117                         ctrl |= WINBIND_CACHED_LOGIN;
118                 else {
119                         _pam_log(LOG_ERR, "pam_parse: unknown option; %s", *argv);
120                 }
121
122         }
123         return ctrl;
124 };
125
126 static void _pam_winbind_cleanup_func(pam_handle_t *pamh, void *data, int error_status)
127 {
128         SAFE_FREE(data);
129 }
130
131 /*
132  * Work around the pam API that has functions with void ** as parameters.
133  * These lead to strict aliasing warnings with gcc.
134  */
135 static int _pam_get_item(const pam_handle_t *pamh, int item_type,
136                          const void *_item)
137 {
138         const void **item = (const void **)_item;
139         return pam_get_item(pamh, item_type, item);
140 }
141 static int _pam_get_data(const pam_handle_t *pamh,
142                          const char *module_data_name, const void *_data)
143 {
144         const void **data = (const void **)_data;
145         return pam_get_data(pamh, module_data_name, data);
146 }
147
148
149 static const struct ntstatus_errors {
150         const char *ntstatus_string;
151         const char *error_string;
152 } ntstatus_errors[] = {
153         {"NT_STATUS_OK", "Success"},
154         {"NT_STATUS_BACKUP_CONTROLLER", "No primary Domain Controler available"},
155         {"NT_STATUS_PWD_TOO_SHORT", "Password too short"},
156         {"NT_STATUS_PWD_TOO_RECENT", "The password of this user is too recent to change"},
157         {"NT_STATUS_PWD_HISTORY_CONFLICT", "Password is already in password history"},
158         {"NT_STATUS_PASSWORD_EXPIRED", "Your password has expired"},
159         {"NT_STATUS_PASSWORD_MUST_CHANGE", "You need to change your password now"},
160         {"NT_STATUS_INVALID_WORKSTATION", "You are not allowed to logon from this workstation"},
161         {"NT_STATUS_INVALID_LOGON_HOURS", "You are not allowed to logon at this time"},
162         {"NT_STATUS_ACCOUNT_EXPIRED", "Your account has expired. Please contact your System administrator"}, /* SCNR */
163         {"NT_STATUS_ACCOUNT_DISABLED", "Your account is disabled. Please contact your System administrator"}, /* SCNR */
164         {"NT_STATUS_ACCOUNT_LOCKED_OUT", "Your account has been locked. Please contact your System administrator"}, /* SCNR */
165         {"NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT", "Invalid Trust Account"},
166         {"NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT", "Invalid Trust Account"},
167         {"NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT", "Invalid Trust Account"},
168         {"NT_STATUS_ACCESS_DENIED", "Access is denied"},
169         {NULL, NULL}
170 };
171
172 const char *_get_ntstatus_error_string(const char *nt_status_string) 
173 {
174         int i;
175         for (i=0; ntstatus_errors[i].ntstatus_string != NULL; i++) {
176                 if (!strcasecmp(ntstatus_errors[i].ntstatus_string, nt_status_string)) {
177                         return ntstatus_errors[i].error_string;
178                 }
179         }
180         return NULL;
181 }
182
183 /* --- authentication management functions --- */
184
185 /* Attempt a conversation */
186
187 static int converse(pam_handle_t *pamh, int nargs,
188                     struct pam_message **message,
189                     struct pam_response **response)
190 {
191         int retval;
192         struct pam_conv *conv;
193
194         retval = _pam_get_item(pamh, PAM_CONV, &conv );
195         if (retval == PAM_SUCCESS) {
196                 retval = conv->conv(nargs, (const struct pam_message **)message,
197                                     response, conv->appdata_ptr);
198         }
199         
200         return retval; /* propagate error status */
201 }
202
203
204 static int _make_remark(pam_handle_t * pamh, int type, const char *text)
205 {
206         int retval = PAM_SUCCESS;
207
208         struct pam_message *pmsg[1], msg[1];
209         struct pam_response *resp;
210         
211         pmsg[0] = &msg[0];
212         msg[0].msg = CONST_DISCARD(char *, text);
213         msg[0].msg_style = type;
214         
215         resp = NULL;
216         retval = converse(pamh, 1, pmsg, &resp);
217         
218         if (resp) {
219                 _pam_drop_reply(resp, 1);
220         }
221         return retval;
222 }
223
224 static int _make_remark_format(pam_handle_t * pamh, int type, const char *format, ...)
225 {
226         va_list args;
227         char *var;
228
229         va_start(args, format);
230         vasprintf(&var, format, args);
231         va_end(args);
232
233         return _make_remark(pamh, type, var);
234 }
235
236 static int pam_winbind_request(pam_handle_t * pamh, int ctrl,
237                                enum winbindd_cmd req_type,
238                                struct winbindd_request *request,
239                                struct winbindd_response *response)
240 {
241         /* Fill in request and send down pipe */
242         init_request(request, req_type);
243         
244         if (write_sock(request, sizeof(*request), 0) == -1) {
245                 _pam_log(LOG_ERR, "write to socket failed!");
246                 close_sock();
247                 return PAM_SERVICE_ERR;
248         }
249         
250         /* Wait for reply */
251         if (read_reply(response) == -1) {
252                 _pam_log(LOG_ERR, "read from socket failed!");
253                 close_sock();
254                 return PAM_SERVICE_ERR;
255         }
256
257         /* We are done with the socket - close it and avoid mischeif */
258         close_sock();
259
260         /* Copy reply data from socket */
261         if (response->result != WINBINDD_OK) {
262                 if (response->data.auth.pam_error != PAM_SUCCESS) {
263                         _pam_log(LOG_ERR, "request failed: %s, PAM error was %s (%d), NT error was %s", 
264                                  response->data.auth.error_string,
265                                  pam_strerror(pamh, response->data.auth.pam_error),
266                                  response->data.auth.pam_error,
267                                  response->data.auth.nt_status_string);
268                         return response->data.auth.pam_error;
269                 } else {
270                         _pam_log(LOG_ERR, "request failed, but PAM error 0!");
271                         return PAM_SERVICE_ERR;
272                 }
273         }
274
275         return PAM_SUCCESS;
276 }
277
278 static int pam_winbind_request_log(pam_handle_t * pamh, 
279                                    int ctrl,
280                                    enum winbindd_cmd req_type,
281                                    struct winbindd_request *request,
282                                    struct winbindd_response *response,
283                                    const char *user)
284 {
285         int retval;
286
287         retval = pam_winbind_request(pamh, ctrl, req_type, request, response);
288
289         switch (retval) {
290         case PAM_AUTH_ERR:
291                 /* incorrect password */
292                 _pam_log(LOG_WARNING, "user `%s' denied access (incorrect password or invalid membership)", user);
293                 return retval;
294         case PAM_ACCT_EXPIRED:
295                 /* account expired */
296                 _pam_log(LOG_WARNING, "user `%s' account expired", user);
297                 return retval;
298         case PAM_AUTHTOK_EXPIRED:
299                 /* password expired */
300                 _pam_log(LOG_WARNING, "user `%s' password expired", user);
301                 return retval;
302         case PAM_NEW_AUTHTOK_REQD:
303                 /* new password required */
304                 _pam_log(LOG_WARNING, "user `%s' new password required", user);
305                 return retval;
306         case PAM_USER_UNKNOWN:
307                 /* the user does not exist */
308                 _pam_log_debug(ctrl, LOG_NOTICE, "user `%s' not found", user);
309                 if (ctrl & WINBIND_UNKNOWN_OK_ARG) {
310                         return PAM_IGNORE;
311                 }        
312                 return retval;
313         case PAM_SUCCESS:
314                 if (req_type == WINBINDD_PAM_AUTH) {
315                         /* Otherwise, the authentication looked good */
316                         _pam_log(LOG_NOTICE, "user '%s' granted access", user);
317                 } else if (req_type == WINBINDD_PAM_CHAUTHTOK) {
318                         /* Otherwise, the authentication looked good */
319                         _pam_log(LOG_NOTICE, "user '%s' password changed", user);
320                 } else { 
321                         /* Otherwise, the authentication looked good */
322                         _pam_log(LOG_NOTICE, "user '%s' OK", user);
323                 }
324         
325                 return retval;
326         default:
327                 /* we don't know anything about this return value */
328                 _pam_log(LOG_ERR, "internal module error (retval = %d, user = `%s')",
329                          retval, user);
330                 return retval;
331         }
332 }
333
334 /* talk to winbindd */
335 static int winbind_auth_request(pam_handle_t * pamh, 
336                                 int ctrl, 
337                                 const char *user, 
338                                 const char *pass, 
339                                 const char *member, 
340                                 const char *cctype,
341                                 int process_result,
342                                 time_t *pwd_last_set)
343 {
344         struct winbindd_request request;
345         struct winbindd_response response;
346         int ret;
347
348         ZERO_STRUCT(request);
349         ZERO_STRUCT(response);
350
351         if (pwd_last_set) {
352                 *pwd_last_set = 0;
353         }
354
355         strncpy(request.data.auth.user, user, 
356                 sizeof(request.data.auth.user)-1);
357
358         strncpy(request.data.auth.pass, pass, 
359                 sizeof(request.data.auth.pass)-1);
360
361         request.data.auth.krb5_cc_type[0] = '\0';
362         request.data.auth.uid = -1;
363         
364         request.flags = WBFLAG_PAM_INFO3_TEXT | WBFLAG_PAM_GET_PWD_POLICY;
365
366         if (ctrl & WINBIND_KRB5_AUTH) {
367
368                 struct passwd *pwd = NULL;
369
370                 _pam_log_debug(ctrl, LOG_DEBUG, "enabling krb5 login flag\n"); 
371
372                 request.flags |= WBFLAG_PAM_KRB5 | WBFLAG_PAM_FALLBACK_AFTER_KRB5;
373
374                 pwd = getpwnam(user);
375                 if (pwd == NULL) {
376                         return PAM_USER_UNKNOWN;
377                 }
378                 request.data.auth.uid = pwd->pw_uid;
379         }
380
381         if (ctrl & WINBIND_CACHED_LOGIN) {
382                 _pam_log_debug(ctrl, LOG_DEBUG, "enabling cached login flag\n"); 
383                 request.flags |= WBFLAG_PAM_CACHED_LOGIN;
384         }
385
386         if (cctype != NULL) {
387                 strncpy(request.data.auth.krb5_cc_type, cctype, 
388                         sizeof(request.data.auth.krb5_cc_type) - 1);
389                 _pam_log_debug(ctrl, LOG_DEBUG, "enabling request for a %s krb5 ccache\n", cctype); 
390         }
391
392         request.data.auth.require_membership_of_sid[0] = '\0';
393
394         if (member != NULL) {
395                 strncpy(request.data.auth.require_membership_of_sid, member, 
396                         sizeof(request.data.auth.require_membership_of_sid)-1);
397         }
398
399         /* lookup name? */ 
400         if ( (member != NULL) && (strncmp("S-", member, 2) != 0) ) {
401                 
402                 struct winbindd_request sid_request;
403                 struct winbindd_response sid_response;
404
405                 ZERO_STRUCT(sid_request);
406                 ZERO_STRUCT(sid_response);
407
408                 _pam_log_debug(ctrl, LOG_DEBUG, "no sid given, looking up: %s\n", member);
409
410                 /* fortunatly winbindd can handle non-separated names */
411                 strncpy(sid_request.data.name.name, member,
412                         sizeof(sid_request.data.name.name) - 1);
413
414                 if (pam_winbind_request_log(pamh, ctrl, WINBINDD_LOOKUPNAME, &sid_request, &sid_response, user)) {
415                         _pam_log(LOG_INFO, "could not lookup name: %s\n", member); 
416                         return PAM_AUTH_ERR;
417                 }
418
419                 member = sid_response.data.sid.sid;
420
421                 strncpy(request.data.auth.require_membership_of_sid, member, 
422                         sizeof(request.data.auth.require_membership_of_sid)-1);
423         }
424         
425         ret = pam_winbind_request_log(pamh, ctrl, WINBINDD_PAM_AUTH, &request, &response, user);
426
427         if (pwd_last_set) {
428                 *pwd_last_set = response.data.auth.info3.pass_last_set_time;
429         }
430
431         if ((ctrl & WINBIND_KRB5_AUTH) && 
432             response.data.auth.krb5ccname[0] != '\0') {
433
434                 char var[PATH_MAX];
435
436                 _pam_log_debug(ctrl, LOG_DEBUG, "request returned KRB5CCNAME: %s", 
437                                response.data.auth.krb5ccname);
438         
439                 snprintf(var, sizeof(var), "KRB5CCNAME=%s", response.data.auth.krb5ccname);
440         
441                 ret = pam_putenv(pamh, var);
442                 if (ret != PAM_SUCCESS) {
443                         _pam_log(LOG_ERR, "failed to set KRB5CCNAME to %s", var);
444                         return ret;
445                 }
446         }
447
448         if (!process_result) {
449                 return ret;
450         }
451
452         if (ret) {
453                 PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_PASSWORD_EXPIRED");
454                 PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_PASSWORD_MUST_CHANGE");
455                 PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_INVALID_WORKSTATION");
456                 PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_INVALID_LOGON_HOURS");
457                 PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_ACCOUNT_EXPIRED");
458                 PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_ACCOUNT_DISABLED");
459                 PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_ACCOUNT_LOCKED_OUT");
460                 PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT");
461                 PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT");
462                 PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT");
463         }
464
465         /* handle the case where the auth was ok, but the password must expire right now */
466         /* good catch from Ralf Haferkamp: an expiry of "never" is translated to -1 */
467         if ( ! (response.data.auth.info3.acct_flags & ACB_PWNOEXP) &&
468             (response.data.auth.policy.expire > 0) && 
469             (response.data.auth.info3.pass_last_set_time + response.data.auth.policy.expire < time(NULL))) {
470
471                 ret = PAM_AUTHTOK_EXPIRED;
472
473                 _pam_log_debug(ctrl, LOG_DEBUG,"Password has expired (Password was last set: %d, "
474                                "the policy says it should expire here %d (now it's: %d)\n",
475                                response.data.auth.info3.pass_last_set_time,
476                                response.data.auth.info3.pass_last_set_time + response.data.auth.policy.expire,
477                                time(NULL));
478
479                 PAM_WB_REMARK_DIRECT_RET(pamh, "NT_STATUS_PASSWORD_EXPIRED");
480
481         }
482
483         /* warn a user if the password is about to expire soon */
484         if ( ! (response.data.auth.info3.acct_flags & ACB_PWNOEXP) &&
485             (response.data.auth.policy.expire) && 
486             (response.data.auth.info3.pass_last_set_time + response.data.auth.policy.expire > time(NULL) ) ) {
487
488                 int days = response.data.auth.policy.expire / SECONDS_PER_DAY;
489                 if (days <= DAYS_TO_WARN_BEFORE_PWD_EXPIRES) {
490                         _make_remark_format(pamh, PAM_TEXT_INFO, "Your password will expire in %d days", days);
491                 }
492         }
493
494         if (response.data.auth.info3.user_flgs & LOGON_CACHED_ACCOUNT) {
495                 _make_remark(pamh, PAM_ERROR_MSG, "Logging on using cached account. Network ressources can be unavailable");
496                 _pam_log_debug(ctrl, LOG_DEBUG,"User %s logged on using cached account\n", user);
497         }
498
499         /* save the CIFS homedir for pam_cifs / pam_mount */
500         if (response.data.auth.info3.home_dir[0] != '\0') {
501                 char *buf;
502
503                 if (!asprintf(&buf, "%s", response.data.auth.info3.home_dir)) {
504                         return PAM_BUF_ERR;
505                 }
506
507                 pam_set_data( pamh, PAM_WINBIND_HOMEDIR, (void *)buf, _pam_winbind_cleanup_func);
508         }
509
510         return ret;
511 }
512
513 /* talk to winbindd */
514 static int winbind_chauthtok_request(pam_handle_t * pamh,
515                                      int ctrl,
516                                      const char *user, 
517                                      const char *oldpass,
518                                      const char *newpass,
519                                      time_t pwd_last_set) 
520 {
521         struct winbindd_request request;
522         struct winbindd_response response;
523         int ret;
524
525         ZERO_STRUCT(request);
526         ZERO_STRUCT(response);
527
528         if (request.data.chauthtok.user == NULL) return -2;
529
530         strncpy(request.data.chauthtok.user, user, 
531                 sizeof(request.data.chauthtok.user) - 1);
532
533         if (oldpass != NULL) {
534                 strncpy(request.data.chauthtok.oldpass, oldpass, 
535                         sizeof(request.data.chauthtok.oldpass) - 1);
536         } else {
537                 request.data.chauthtok.oldpass[0] = '\0';
538         }
539         
540         if (newpass != NULL) {
541                 strncpy(request.data.chauthtok.newpass, newpass, 
542                         sizeof(request.data.chauthtok.newpass) - 1);
543         } else {
544                 request.data.chauthtok.newpass[0] = '\0';
545         }
546
547         if (ctrl & WINBIND_KRB5_AUTH) {
548                 request.flags = WBFLAG_PAM_KRB5;
549         }
550
551         ret = pam_winbind_request_log(pamh, ctrl, WINBINDD_PAM_CHAUTHTOK, &request, &response, user);
552
553         if (ret == PAM_SUCCESS) {
554                 return ret;
555         }
556
557         PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_BACKUP_CONTROLLER");
558         PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_ACCESS_DENIED");
559
560         /* TODO: tell the min pwd length ? */
561         PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_PWD_TOO_SHORT");
562
563         /* TODO: tell the minage ? */
564         PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_PWD_TOO_RECENT");
565
566         /* TODO: tell the history length ? */
567         PAM_WB_REMARK_CHECK_RESPONSE_RET(pamh, response, "NT_STATUS_PWD_HISTORY_CONFLICT");
568
569         if (!strcasecmp(response.data.auth.nt_status_string, "NT_STATUS_PASSWORD_RESTRICTION")) {
570
571                 /* FIXME: avoid to send multiple PAM messages after another */
572                 switch (response.data.auth.reject_reason) {
573                         case -1:
574                                 break;
575                         case REJECT_REASON_OTHER:
576                                 if ((response.data.auth.policy.min_passwordage > 0) &&
577                                     (pwd_last_set + response.data.auth.policy.min_passwordage > time(NULL))) {
578                                         PAM_WB_REMARK_DIRECT(pamh, "NT_STATUS_PWD_TOO_RECENT");
579                                 }
580                                 break;
581                         case REJECT_REASON_TOO_SHORT:
582                                 PAM_WB_REMARK_DIRECT(pamh, "NT_STATUS_PWD_TOO_SHORT");
583                                 break;
584                         case REJECT_REASON_IN_HISTORY:
585                                 PAM_WB_REMARK_DIRECT(pamh, "NT_STATUS_PWD_HISTORY_CONFLICT");
586                                 break;
587                         case REJECT_REASON_NOT_COMPLEX:
588                                 _make_remark(pamh, PAM_ERROR_MSG, "Password does not meet complexity requirements");
589                                 break;
590                         default:
591                                 _pam_log_debug(ctrl, LOG_DEBUG,
592                                                "unknown password change reject reason: %d", 
593                                                response.data.auth.reject_reason);
594                                 break;
595                 }
596
597                 _make_remark_format(pamh, PAM_ERROR_MSG,  
598                         "Your password must be at least %d characters; "
599                         "cannot repeat any of the your previous %d passwords"
600                         "%s. "
601                         "Please type a different password. "
602                         "Type a password which meets these requirements in both text boxes.",
603                         response.data.auth.policy.min_length_password,
604                         response.data.auth.policy.password_history,
605                         (response.data.auth.policy.password_properties & DOMAIN_PASSWORD_COMPLEX) ? 
606                                 "; must contain capitals, numerals or punctuation; and cannot contain your account or full name" : 
607                                 "");
608
609         }
610
611         return ret;
612 }
613
614 /*
615  * Checks if a user has an account
616  *
617  * return values:
618  *       1  = User not found
619  *       0  = OK
620  *      -1  = System error
621  */
622 static int valid_user(const char *user, pam_handle_t *pamh, int ctrl)
623 {
624         /* check not only if the user is available over NSS calls, also make
625          * sure it's really a winbind user, this is important when stacking PAM
626          * modules in the 'account' or 'password' facility. */
627
628         struct passwd *pwd = NULL;
629         struct winbindd_request request;
630         struct winbindd_response response;
631         int ret;
632
633         ZERO_STRUCT(request);
634         ZERO_STRUCT(response);
635
636         pwd = getpwnam(user);
637         if (pwd == NULL) {
638                 return 1;
639         }
640
641         strncpy(request.data.username, user,
642                 sizeof(request.data.username) - 1);
643
644         ret = pam_winbind_request_log(pamh, ctrl, WINBINDD_GETPWNAM, &request, &response, user);
645
646         switch (ret) {
647                 case PAM_USER_UNKNOWN:
648                         return 1;
649                 case PAM_SUCCESS:
650                         return 0;
651                 default:
652                         break;
653         }
654         return -1;
655 }
656
657 static char *_pam_delete(register char *xx)
658 {
659         _pam_overwrite(xx);
660         _pam_drop(xx);
661         return NULL;
662 }
663
664 /*
665  * obtain a password from the user
666  */
667
668 static int _winbind_read_password(pam_handle_t * pamh,
669                                   unsigned int ctrl,
670                                   const char *comment,
671                                   const char *prompt1,
672                                   const char *prompt2,
673                                   const char **pass)
674 {
675         int authtok_flag;
676         int retval;
677         const char *item;
678         char *token;
679
680         /*
681          * make sure nothing inappropriate gets returned
682          */
683
684         *pass = token = NULL;
685
686         /*
687          * which authentication token are we getting?
688          */
689
690         authtok_flag = on(WINBIND__OLD_PASSWORD, ctrl) ? PAM_OLDAUTHTOK : PAM_AUTHTOK;
691
692         /*
693          * should we obtain the password from a PAM item ?
694          */
695
696         if (on(WINBIND_TRY_FIRST_PASS_ARG, ctrl) || on(WINBIND_USE_FIRST_PASS_ARG, ctrl)) {
697                 retval = _pam_get_item(pamh, authtok_flag, &item);
698                 if (retval != PAM_SUCCESS) {
699                         /* very strange. */
700                         _pam_log(LOG_ALERT, 
701                                  "pam_get_item returned error to unix-read-password"
702                             );
703                         return retval;
704                 } else if (item != NULL) {      /* we have a password! */
705                         *pass = item;
706                         item = NULL;
707                         return PAM_SUCCESS;
708                 } else if (on(WINBIND_USE_FIRST_PASS_ARG, ctrl)) {
709                         return PAM_AUTHTOK_RECOVER_ERR;         /* didn't work */
710                 } else if (on(WINBIND_USE_AUTHTOK_ARG, ctrl)
711                            && off(WINBIND__OLD_PASSWORD, ctrl)) {
712                         return PAM_AUTHTOK_RECOVER_ERR;
713                 }
714         }
715         /*
716          * getting here implies we will have to get the password from the
717          * user directly.
718          */
719
720         {
721                 struct pam_message msg[3], *pmsg[3];
722                 struct pam_response *resp;
723                 int i, replies;
724
725                 /* prepare to converse */
726
727                 if (comment != NULL) {
728                         pmsg[0] = &msg[0];
729                         msg[0].msg_style = PAM_TEXT_INFO;
730                         msg[0].msg = CONST_DISCARD(char *, comment);
731                         i = 1;
732                 } else {
733                         i = 0;
734                 }
735
736                 pmsg[i] = &msg[i];
737                 msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
738                 msg[i++].msg = CONST_DISCARD(char *, prompt1);
739                 replies = 1;
740
741                 if (prompt2 != NULL) {
742                         pmsg[i] = &msg[i];
743                         msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
744                         msg[i++].msg = CONST_DISCARD(char *, prompt2);
745                         ++replies;
746                 }
747                 /* so call the conversation expecting i responses */
748                 resp = NULL;
749                 retval = converse(pamh, i, pmsg, &resp);
750
751                 if (resp != NULL) {
752
753                         /* interpret the response */
754
755                         if (retval == PAM_SUCCESS) {    /* a good conversation */
756
757                                 token = x_strdup(resp[i - replies].resp);
758                                 if (token != NULL) {
759                                         if (replies == 2) {
760                                                 /* verify that password entered correctly */
761                                                 if (!resp[i - 1].resp
762                                                     || strcmp(token, resp[i - 1].resp)) {
763                                                         _pam_delete(token);     /* mistyped */
764                                                         retval = PAM_AUTHTOK_RECOVER_ERR;
765                                                         _make_remark(pamh, PAM_ERROR_MSG, MISTYPED_PASS);
766                                                 }
767                                         }
768                                 } else {
769                                         _pam_log(LOG_NOTICE, "could not recover authentication token");
770                                         retval = PAM_AUTHTOK_RECOVER_ERR;
771                                 }
772
773                         }
774                         /*
775                          * tidy up the conversation (resp_retcode) is ignored
776                          * -- what is it for anyway? AGM
777                          */
778
779                         _pam_drop_reply(resp, i);
780
781                 } else {
782                         retval = (retval == PAM_SUCCESS)
783                             ? PAM_AUTHTOK_RECOVER_ERR : retval;
784                 }
785         }
786
787         if (retval != PAM_SUCCESS) {
788                 _pam_log_debug(ctrl, LOG_DEBUG,
789                                  "unable to obtain a password");
790                 return retval;
791         }
792         /* 'token' is the entered password */
793
794         /* we store this password as an item */
795         
796         retval = pam_set_item(pamh, authtok_flag, token);
797         _pam_delete(token);     /* clean it up */
798         if (retval != PAM_SUCCESS || 
799             (retval = _pam_get_item(pamh, authtok_flag, &item)) != PAM_SUCCESS) {
800                 
801                 _pam_log(LOG_CRIT, "error manipulating password");
802                 return retval;
803                 
804         }
805
806         *pass = item;
807         item = NULL;            /* break link to password */
808
809         return PAM_SUCCESS;
810 }
811
812 const char *get_conf_item_string(int argc, 
813                                  const char **argv, 
814                                  int ctrl,
815                                  dictionary *d,
816                                  const char *item, 
817                                  int flag)
818 {
819         int i = 0;
820         char *parm = NULL;
821         const char *parm_opt = NULL;
822         char *key = NULL;
823
824         if (!(ctrl & flag)) {
825                 goto out;
826         }
827
828         /* let the pam opt take precedence over the pam_winbind.conf option */
829
830         if (d != NULL) {
831
832                 if (!asprintf(&key, "global:%s", item)) {
833                         goto out;
834                 }
835
836                 parm_opt = iniparser_getstr(d, key);
837                 SAFE_FREE(key);
838         }
839
840         for ( i=0; i<argc; i++ ) {
841
842                 if ((strncmp(argv[i], item, strlen(item)) == 0)) {
843                         char *p;
844
845                         parm = strdup(argv[i]);
846
847                         if ( (p = strchr( parm, '=' )) == NULL) {
848                                 _pam_log(LOG_INFO, "no \"=\" delimiter for \"%s\" found\n", item);
849                                 goto out;
850                         }
851                         SAFE_FREE(parm);
852                         _pam_log_debug(ctrl, LOG_INFO, "PAM config: %s '%s'\n", item, p+1);
853                         return p + 1;
854                 }
855         }
856
857         if (d != NULL) {
858                 _pam_log_debug(ctrl, LOG_INFO, "CONFIG file: %s '%s'\n", item, parm_opt);
859         }
860 out:
861         SAFE_FREE(parm);
862         return parm_opt;
863 }
864
865 const char *get_krb5_cc_type_from_config(int argc, const char **argv, int ctrl, dictionary *d)
866 {
867         return get_conf_item_string(argc, argv, ctrl, d, "krb5_ccache_type", WINBIND_KRB5_CCACHE_TYPE);
868 }
869
870 const char *get_member_from_config(int argc, const char **argv, int ctrl, dictionary *d)
871 {
872         const char *ret = NULL;
873         ret = get_conf_item_string(argc, argv, ctrl, d, "require_membership_of", WINBIND_REQUIRED_MEMBERSHIP);
874         if (ret) {
875                 return ret;
876         }
877         return get_conf_item_string(argc, argv, ctrl, d, "require-membership-of", WINBIND_REQUIRED_MEMBERSHIP);
878 }
879
880 PAM_EXTERN
881 int pam_sm_authenticate(pam_handle_t *pamh, int flags,
882                         int argc, const char **argv)
883 {
884         const char *username;
885         const char *password;
886         const char *member = NULL;
887         const char *cctype = NULL;
888         int retval = PAM_AUTH_ERR;
889         dictionary *d;
890
891         /* parse arguments */
892         int ctrl = _pam_parse(argc, argv, &d);
893         if (ctrl == -1) {
894                 retval = PAM_SYSTEM_ERR;
895                 goto out;
896         }
897
898         _pam_log_debug(ctrl, LOG_DEBUG,"pam_winbind: pam_sm_authenticate (flags: 0x%04x)", flags);
899
900         /* Get the username */
901         retval = pam_get_user(pamh, &username, NULL);
902         if ((retval != PAM_SUCCESS) || (!username)) {
903                 _pam_log_debug(ctrl, LOG_DEBUG, "can not get the username");
904                 retval = PAM_SERVICE_ERR;
905                 goto out;
906         }
907
908         retval = _winbind_read_password(pamh, ctrl, NULL, 
909                                         "Password: ", NULL,
910                                         &password);
911
912         if (retval != PAM_SUCCESS) {
913                 _pam_log(LOG_ERR, "Could not retrieve user's password");
914                 retval = PAM_AUTHTOK_ERR;
915                 goto out;
916         }
917
918         /* Let's not give too much away in the log file */
919
920 #ifdef DEBUG_PASSWORD
921         _pam_log_debug(ctrl, LOG_INFO, "Verify user `%s' with password `%s'", 
922                        username, password);
923 #else
924         _pam_log_debug(ctrl, LOG_INFO, "Verify user `%s'", username);
925 #endif
926
927         member = get_member_from_config(argc, argv, ctrl, d);
928
929         cctype = get_krb5_cc_type_from_config(argc, argv, ctrl, d);
930
931         /* Now use the username to look up password */
932         retval = winbind_auth_request(pamh, ctrl, username, password, member, cctype, True, NULL);
933
934         if (retval == PAM_NEW_AUTHTOK_REQD ||
935             retval == PAM_AUTHTOK_EXPIRED) {
936
937                 char *buf;
938
939                 if (!asprintf(&buf, "%d", retval)) {
940                         retval = PAM_BUF_ERR;
941                         goto out;
942                 }
943
944                 pam_set_data( pamh, PAM_WINBIND_NEW_AUTHTOK_REQD, (void *)buf, _pam_winbind_cleanup_func);
945
946                 retval = PAM_SUCCESS;
947                 goto out;
948         }
949
950 out:
951         if (d) {
952                 iniparser_freedict(d);
953         }
954         return retval;
955 }
956
957 PAM_EXTERN
958 int pam_sm_setcred(pam_handle_t *pamh, int flags,
959                    int argc, const char **argv)
960 {
961         /* parse arguments */
962         int ctrl = _pam_parse(argc, argv, NULL);
963         if (ctrl == -1) {
964                 return PAM_SYSTEM_ERR;
965         }
966
967         _pam_log_debug(ctrl, LOG_DEBUG,"pam_winbind: pam_sm_setcred (flags: 0x%04x)", flags);
968
969         if (flags & PAM_DELETE_CRED) {
970                 return pam_sm_close_session(pamh, flags, argc, argv);
971         }
972
973         return PAM_SUCCESS;
974 }
975
976 /*
977  * Account management. We want to verify that the account exists 
978  * before returning PAM_SUCCESS
979  */
980 PAM_EXTERN
981 int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
982                    int argc, const char **argv)
983 {
984         const char *username;
985         int retval = PAM_USER_UNKNOWN;
986         void *tmp = NULL;
987
988         /* parse arguments */
989         int ctrl = _pam_parse(argc, argv, NULL);
990         if (ctrl == -1) {
991                 return PAM_SYSTEM_ERR;
992         }
993
994         _pam_log_debug(ctrl, LOG_DEBUG,"pam_winbind: pam_sm_acct_mgmt (flags: 0x%04x)", flags);
995
996
997         /* Get the username */
998         retval = pam_get_user(pamh, &username, NULL);
999         if ((retval != PAM_SUCCESS) || (!username)) {
1000                 _pam_log_debug(ctrl, LOG_DEBUG,"can not get the username");
1001                 return PAM_SERVICE_ERR;
1002         }
1003
1004         /* Verify the username */
1005         retval = valid_user(username, pamh, ctrl);
1006         switch (retval) {
1007         case -1:
1008                 /* some sort of system error. The log was already printed */
1009                 return PAM_SERVICE_ERR;
1010         case 1:
1011                 /* the user does not exist */
1012                 _pam_log_debug(ctrl, LOG_NOTICE, "user `%s' not found", username);
1013                 if (ctrl & WINBIND_UNKNOWN_OK_ARG) {
1014                         return PAM_IGNORE;
1015                 }
1016                 return PAM_USER_UNKNOWN;
1017         case 0:
1018                 pam_get_data( pamh, PAM_WINBIND_NEW_AUTHTOK_REQD, (const void **)&tmp);
1019                 if (tmp != NULL) {
1020                         retval = atoi(tmp);
1021                         switch (retval) {
1022                         case PAM_AUTHTOK_EXPIRED:
1023                                 /* fall through, since new token is required in this case */
1024                         case PAM_NEW_AUTHTOK_REQD:
1025                                 _pam_log(LOG_WARNING, "pam_sm_acct_mgmt success but %s is set", 
1026                                          PAM_WINBIND_NEW_AUTHTOK_REQD);
1027                                 _pam_log(LOG_NOTICE, "user '%s' needs new password", username);
1028                                 /* PAM_AUTHTOKEN_REQD does not exist, but is documented in the manpage */
1029                                 return PAM_NEW_AUTHTOK_REQD; 
1030                         default:
1031                                 _pam_log(LOG_WARNING, "pam_sm_acct_mgmt success");
1032                                 _pam_log(LOG_NOTICE, "user '%s' granted access", username);
1033                                 return PAM_SUCCESS;
1034                         }
1035                 }
1036
1037                 /* Otherwise, the authentication looked good */
1038                 _pam_log(LOG_NOTICE, "user '%s' granted access", username);
1039                 return PAM_SUCCESS;
1040         default:
1041                 /* we don't know anything about this return value */
1042                 _pam_log(LOG_ERR, "internal module error (retval = %d, user = `%s')", 
1043                          retval, username);
1044                 return PAM_SERVICE_ERR;
1045         }
1046
1047         /* should not be reached */
1048         return PAM_IGNORE;
1049 }
1050
1051 PAM_EXTERN
1052 int pam_sm_open_session(pam_handle_t *pamh, int flags,
1053                         int argc, const char **argv)
1054 {
1055         /* parse arguments */
1056         int ctrl = _pam_parse(argc, argv, NULL);
1057         if (ctrl == -1) {
1058                 return PAM_SYSTEM_ERR;
1059         }
1060
1061         _pam_log_debug(ctrl, LOG_DEBUG,"pam_winbind: pam_sm_open_session handler (flags: 0x%04x)", flags);
1062
1063         return PAM_SUCCESS;
1064 }
1065
1066 PAM_EXTERN
1067 int pam_sm_close_session(pam_handle_t *pamh, int flags,
1068                          int argc, const char **argv)
1069 {
1070         dictionary *d;
1071         int retval = PAM_SUCCESS;
1072
1073         /* parse arguments */
1074         int ctrl = _pam_parse(argc, argv, &d);
1075         if (ctrl == -1) {
1076                 retval = PAM_SYSTEM_ERR;
1077                 goto out;
1078         }
1079
1080         _pam_log_debug(ctrl, LOG_DEBUG,"pam_winbind: pam_sm_close_session handler (flags: 0x%04x)", flags);
1081
1082         if (!(flags & PAM_DELETE_CRED)) {
1083                 retval = PAM_SUCCESS;
1084                 goto out;
1085         }
1086
1087         if (ctrl & WINBIND_KRB5_AUTH) {
1088
1089                 /* destroy the ccache here */
1090                 struct winbindd_request request;
1091                 struct winbindd_response response;
1092                 const char *user;
1093                 const char *ccname = NULL;
1094                 struct passwd *pwd = NULL;
1095
1096                 ZERO_STRUCT(request);
1097                 ZERO_STRUCT(response);
1098
1099                 retval = pam_get_user(pamh, &user, "Username: ");
1100                 if (retval == PAM_SUCCESS) {
1101                         if (user == NULL) {
1102                                 _pam_log(LOG_ERR, "username was NULL!");
1103                                 retval = PAM_USER_UNKNOWN;
1104                                 goto out;
1105                         }
1106                         if (retval == PAM_SUCCESS) {
1107                                 _pam_log_debug(ctrl, LOG_DEBUG, "username [%s] obtained", user);
1108                         }
1109                 } else {
1110                         _pam_log_debug(ctrl, LOG_DEBUG, "could not identify user");
1111                         goto out;
1112                 }
1113
1114                 ccname = pam_getenv(pamh, "KRB5CCNAME");
1115                 if (ccname == NULL) {
1116                         _pam_log_debug(ctrl, LOG_DEBUG, "user has no KRB5CCNAME environment");
1117                         retval = PAM_SUCCESS;
1118                         goto out;
1119                 }
1120
1121                 strncpy(request.data.logoff.user, user,
1122                         sizeof(request.data.logoff.user) - 1);
1123
1124                 strncpy(request.data.logoff.krb5ccname, ccname,
1125                         sizeof(request.data.logoff.krb5ccname) - 1);
1126
1127                 pwd = getpwnam(user);
1128                 if (pwd == NULL) {
1129                         retval = PAM_USER_UNKNOWN;
1130                         goto out;
1131                 }
1132                 request.data.logoff.uid = pwd->pw_uid;
1133
1134                 request.flags = WBFLAG_PAM_KRB5;
1135
1136                 retval = pam_winbind_request_log(pamh, ctrl, WINBINDD_PAM_LOGOFF, &request, &response, user);
1137         }
1138
1139 out:
1140         if (d) {
1141                 iniparser_freedict(d);
1142         }
1143         return retval;
1144 }
1145
1146
1147
1148 PAM_EXTERN 
1149 int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
1150                      int argc, const char **argv)
1151 {
1152         unsigned int lctrl;
1153         int retval;
1154         unsigned int ctrl;
1155
1156         /* <DO NOT free() THESE> */
1157         const char *user;
1158         char *pass_old, *pass_new;
1159         /* </DO NOT free() THESE> */
1160
1161         char *Announce;
1162         
1163         int retry = 0;
1164         dictionary *d;
1165
1166         ctrl = _pam_parse(argc, argv, &d);
1167         if (ctrl == -1) {
1168                 retval = PAM_SYSTEM_ERR;
1169                 goto out;
1170         }
1171
1172         _pam_log_debug(ctrl, LOG_DEBUG,"pam_winbind: pam_sm_chauthtok (flags: 0x%04x)", flags);
1173
1174         /* clearing offline bit for the auth in the password change */
1175         ctrl &= ~WINBIND_CACHED_LOGIN;
1176
1177         /*
1178          * First get the name of a user
1179          */
1180         retval = pam_get_user(pamh, &user, "Username: ");
1181         if (retval == PAM_SUCCESS) {
1182                 if (user == NULL) {
1183                         _pam_log(LOG_ERR, "username was NULL!");
1184                         retval = PAM_USER_UNKNOWN;
1185                         goto out;
1186                 }
1187                 if (retval == PAM_SUCCESS) {
1188                         _pam_log_debug(ctrl, LOG_DEBUG, "username [%s] obtained",
1189                                  user);
1190                 }
1191         } else {
1192                 _pam_log_debug(ctrl, LOG_DEBUG,
1193                          "password - could not identify user");
1194                 goto out;
1195         }
1196
1197         /* check if this is really a user in winbindd, not only in NSS */
1198         retval = valid_user(user, pamh, ctrl);
1199         switch (retval) {
1200                 case 1:
1201                         retval = PAM_USER_UNKNOWN;
1202                         goto out;
1203                 case -1:
1204                         retval = PAM_SYSTEM_ERR;
1205                         goto out;
1206                 default:
1207                         break;
1208         }
1209                 
1210         /*
1211          * obtain and verify the current password (OLDAUTHTOK) for
1212          * the user.
1213          */
1214
1215         if (flags & PAM_PRELIM_CHECK) {
1216                 
1217                 time_t pwdlastset_prelim = 0;
1218                 
1219                 /* instruct user what is happening */
1220 #define greeting "Changing password for "
1221                 Announce = (char *) malloc(sizeof(greeting) + strlen(user));
1222                 if (Announce == NULL) {
1223                         _pam_log(LOG_CRIT, "password - out of memory");
1224                         retval = PAM_BUF_ERR;
1225                         goto out;
1226                 }
1227                 (void) strcpy(Announce, greeting);
1228                 (void) strcpy(Announce + sizeof(greeting) - 1, user);
1229 #undef greeting
1230                 
1231                 lctrl = ctrl | WINBIND__OLD_PASSWORD;
1232                 retval = _winbind_read_password(pamh, lctrl,
1233                                                 Announce,
1234                                                 "(current) NT password: ",
1235                                                 NULL,
1236                                                 (const char **) &pass_old);
1237                 if (retval != PAM_SUCCESS) {
1238                         _pam_log(LOG_NOTICE, "password - (old) token not obtained");
1239                         goto out;
1240                 }
1241                 /* verify that this is the password for this user */
1242                 
1243                 retval = winbind_auth_request(pamh, ctrl, user, pass_old, NULL, NULL, False, &pwdlastset_prelim);
1244
1245                 if (retval != PAM_ACCT_EXPIRED && 
1246                     retval != PAM_AUTHTOK_EXPIRED &&
1247                     retval != PAM_NEW_AUTHTOK_REQD &&
1248                     retval != PAM_SUCCESS) {
1249                         pass_old = NULL;
1250                         goto out;
1251                 }
1252                 
1253                 pam_set_data(pamh, PAM_WINBIND_PWD_LAST_SET, (void *)pwdlastset_prelim, _pam_winbind_cleanup_func);
1254
1255                 retval = pam_set_item(pamh, PAM_OLDAUTHTOK, (const void *) pass_old);
1256                 pass_old = NULL;
1257                 if (retval != PAM_SUCCESS) {
1258                         _pam_log(LOG_CRIT, "failed to set PAM_OLDAUTHTOK");
1259                 }
1260         } else if (flags & PAM_UPDATE_AUTHTOK) {
1261         
1262                 time_t pwdlastset_update = 0;
1263                 
1264                 /*
1265                  * obtain the proposed password
1266                  */
1267                 
1268                 /*
1269                  * get the old token back. 
1270                  */
1271                 
1272                 retval = _pam_get_item(pamh, PAM_OLDAUTHTOK, &pass_old);
1273                 
1274                 if (retval != PAM_SUCCESS) {
1275                         _pam_log(LOG_NOTICE, "user not authenticated");
1276                         goto out;
1277                 }
1278                 
1279                 lctrl = ctrl;
1280                 
1281                 if (on(WINBIND_USE_AUTHTOK_ARG, lctrl)) {
1282                         lctrl |= WINBIND_USE_FIRST_PASS_ARG;
1283                 }
1284                 retry = 0;
1285                 retval = PAM_AUTHTOK_ERR;
1286                 while ((retval != PAM_SUCCESS) && (retry++ < MAX_PASSWD_TRIES)) {
1287                         /*
1288                          * use_authtok is to force the use of a previously entered
1289                          * password -- needed for pluggable password strength checking
1290                          */
1291                         
1292                         retval = _winbind_read_password(pamh, lctrl,
1293                                                         NULL,
1294                                                         "Enter new NT password: ",
1295                                                         "Retype new NT password: ",
1296                                                         (const char **) &pass_new);
1297                         
1298                         if (retval != PAM_SUCCESS) {
1299                                 _pam_log_debug(ctrl, LOG_ALERT
1300                                          ,"password - new password not obtained");
1301                                 pass_old = NULL;/* tidy up */
1302                                 goto out;
1303                         }
1304
1305                         /*
1306                          * At this point we know who the user is and what they
1307                          * propose as their new password. Verify that the new
1308                          * password is acceptable.
1309                          */
1310                         
1311                         if (pass_new[0] == '\0') {/* "\0" password = NULL */
1312                                 pass_new = NULL;
1313                         }
1314                 }
1315                 
1316                 /*
1317                  * By reaching here we have approved the passwords and must now
1318                  * rebuild the password database file.
1319                  */
1320                 _pam_get_data( pamh, PAM_WINBIND_PWD_LAST_SET,
1321                                &pwdlastset_update);
1322
1323                 retval = winbind_chauthtok_request(pamh, ctrl, user, pass_old, pass_new, pwdlastset_update);
1324                 if (retval) {
1325                         _pam_overwrite(pass_new);
1326                         _pam_overwrite(pass_old);
1327                         pass_old = pass_new = NULL;
1328                         goto out;
1329                 }
1330
1331                 /* just in case we need krb5 creds after a password change over msrpc */
1332
1333                 if (ctrl & WINBIND_KRB5_AUTH) {
1334
1335                         const char *member = get_member_from_config(argc, argv, ctrl, d);
1336                         const char *cctype = get_krb5_cc_type_from_config(argc, argv, ctrl, d);
1337
1338                         retval = winbind_auth_request(pamh, ctrl, user, pass_new, member, cctype, False, NULL);
1339                         _pam_overwrite(pass_new);
1340                         _pam_overwrite(pass_old);
1341                         pass_old = pass_new = NULL;
1342                 }
1343         } else {
1344                 retval = PAM_SERVICE_ERR;
1345         }
1346
1347 out:
1348         if (d) {
1349                 iniparser_freedict(d);
1350         }
1351         return retval;
1352 }
1353
1354 #ifdef PAM_STATIC
1355
1356 /* static module data */
1357
1358 struct pam_module _pam_winbind_modstruct = {
1359         MODULE_NAME,
1360         pam_sm_authenticate,
1361         pam_sm_setcred,
1362         pam_sm_acct_mgmt,
1363         pam_sm_open_session,
1364         pam_sm_close_session,
1365         pam_sm_chauthtok
1366 };
1367
1368 #endif
1369
1370 /*
1371  * Copyright (c) Andrew Tridgell  <tridge@samba.org>   2000
1372  * Copyright (c) Tim Potter       <tpot@samba.org>     2000
1373  * Copyright (c) Andrew Bartlettt <abartlet@samba.org> 2002
1374  * Copyright (c) Guenther Deschner <gd@samba.org>      2005-2006
1375  * Copyright (c) Jan Rêkorajski 1999.
1376  * Copyright (c) Andrew G. Morgan 1996-8.
1377  * Copyright (c) Alex O. Yuriev, 1996.
1378  * Copyright (c) Cristian Gafton 1996.
1379  * Copyright (C) Elliot Lee <sopwith@redhat.com> 1996, Red Hat Software. 
1380  *
1381  * Redistribution and use in source and binary forms, with or without
1382  * modification, are permitted provided that the following conditions
1383  * are met:
1384  * 1. Redistributions of source code must retain the above copyright
1385  *    notice, and the entire permission notice in its entirety,
1386  *    including the disclaimer of warranties.
1387  * 2. Redistributions in binary form must reproduce the above copyright
1388  *    notice, this list of conditions and the following disclaimer in the
1389  *    documentation and/or other materials provided with the distribution.
1390  * 3. The name of the author may not be used to endorse or promote
1391  *    products derived from this software without specific prior
1392  *    written permission.
1393  *
1394  * ALTERNATIVELY, this product may be distributed under the terms of
1395  * the GNU Public License, in which case the provisions of the GPL are
1396  * required INSTEAD OF the above restrictions.  (This clause is
1397  * necessary due to a potential bad interaction between the GPL and
1398  * the restrictions contained in a BSD-style copyright.)
1399  *
1400  * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED
1401  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1402  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
1403  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
1404  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
1405  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
1406  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
1407  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
1408  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
1409  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
1410  * OF THE POSSIBILITY OF SUCH DAMAGE.
1411  */