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