r2779: Some fixes to pam_winbind.c.:
[tprouty/samba.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
7    largely based on pam_userdb by Christian Gafton <gafton@redhat.com> 
8    also contains large slabs of code from pam_unix by Elliot Lee <sopwith@redhat.com>
9    (see copyright below for full details)
10 */
11
12 #include "pam_winbind.h"
13
14 /* data tokens */
15
16 #define MAX_PASSWD_TRIES        3
17
18 /* some syslogging */
19 static void _pam_log(int err, const char *format, ...)
20 {
21         va_list args;
22
23         va_start(args, format);
24         openlog(MODULE_NAME, LOG_CONS|LOG_PID, LOG_AUTH);
25         vsyslog(err, format, args);
26         va_end(args);
27         closelog();
28 }
29
30 static int _pam_parse(int argc, const char **argv)
31 {
32         int ctrl;
33         /* step through arguments */
34         for (ctrl = 0; argc-- > 0; ++argv) {
35
36                 /* generic options */
37                 
38                 if (!strcmp(*argv,"debug"))
39                         ctrl |= WINBIND_DEBUG_ARG;
40                 else if (!strcasecmp(*argv, "use_authtok"))
41                         ctrl |= WINBIND_USE_AUTHTOK_ARG;
42                 else if (!strcasecmp(*argv, "use_first_pass"))
43                         ctrl |= WINBIND_USE_FIRST_PASS_ARG;
44                 else if (!strcasecmp(*argv, "try_first_pass"))
45                         ctrl |= WINBIND_TRY_FIRST_PASS_ARG;
46                 else if (!strcasecmp(*argv, "unknown_ok"))
47                         ctrl |= WINBIND_UNKNOWN_OK_ARG;
48                 else if (!strncasecmp(*argv, "require_membership_of", strlen("require_membership_of")))
49                         ctrl |= WINBIND_REQUIRED_MEMBERSHIP;
50                 else if (!strncasecmp(*argv, "require-membership-of", strlen("require-membership-of")))
51                         ctrl |= WINBIND_REQUIRED_MEMBERSHIP;
52                 else {
53                         _pam_log(LOG_ERR, "pam_parse: unknown option; %s", *argv);
54                 }
55         }
56         
57         return ctrl;
58 }
59
60 /* --- authentication management functions --- */
61
62 /* Attempt a conversation */
63
64 static int converse(pam_handle_t *pamh, int nargs,
65                     struct pam_message **message,
66                     struct pam_response **response)
67 {
68     int retval;
69     struct pam_conv *conv;
70
71     retval = pam_get_item(pamh, PAM_CONV, (const void **) &conv ) ;
72     if (retval == PAM_SUCCESS) {
73         retval = conv->conv(nargs, (const struct pam_message **)message,
74                             response, conv->appdata_ptr);
75     }
76         
77     return retval; /* propagate error status */
78 }
79
80
81 static int _make_remark(pam_handle_t * pamh, int type, const char *text)
82 {
83         int retval = PAM_SUCCESS;
84
85         struct pam_message *pmsg[1], msg[1];
86         struct pam_response *resp;
87         
88         pmsg[0] = &msg[0];
89         msg[0].msg = text;
90         msg[0].msg_style = type;
91         
92         resp = NULL;
93         retval = converse(pamh, 1, pmsg, &resp);
94         
95         if (resp) {
96                 _pam_drop_reply(resp, 1);
97         }
98         return retval;
99 }
100
101 static int pam_winbind_request(enum winbindd_cmd req_type,
102                                struct winbindd_request *request,
103                                struct winbindd_response *response)
104 {
105
106         /* Fill in request and send down pipe */
107         init_request(request, req_type);
108         
109         if (write_sock(request, sizeof(*request)) == -1) {
110                 _pam_log(LOG_ERR, "write to socket failed!");
111                 close_sock();
112                 return PAM_SERVICE_ERR;
113         }
114         
115         /* Wait for reply */
116         if (read_reply(response) == -1) {
117                 _pam_log(LOG_ERR, "read from socket failed!");
118                 close_sock();
119                 return PAM_SERVICE_ERR;
120         }
121
122         /* We are done with the socket - close it and avoid mischeif */
123         close_sock();
124
125         /* Copy reply data from socket */
126         if (response->result != WINBINDD_OK) {
127                 if (response->data.auth.pam_error != PAM_SUCCESS) {
128                         _pam_log(LOG_ERR, "request failed: %s, PAM error was %d, NT error was %s", 
129                                  response->data.auth.error_string,
130                                  response->data.auth.pam_error,
131                                  response->data.auth.nt_status_string);
132                         return response->data.auth.pam_error;
133                 } else {
134                         _pam_log(LOG_ERR, "request failed, but PAM error 0!");
135                         return PAM_SERVICE_ERR;
136                 }
137         }
138         
139         return PAM_SUCCESS;
140 }
141
142 static int pam_winbind_request_log(enum winbindd_cmd req_type,
143                                struct winbindd_request *request,
144                                struct winbindd_response *response,
145                                    int ctrl,
146                                    const char *user)
147 {
148         int retval;
149
150         retval = pam_winbind_request(req_type, request, response);
151
152         switch (retval) {
153         case PAM_AUTH_ERR:
154                 /* incorrect password */
155                 _pam_log(LOG_WARNING, "user `%s' denied access (incorrect password or invalid membership)", user);
156                 return retval;
157         case PAM_ACCT_EXPIRED:
158                 /* account expired */
159                 _pam_log(LOG_WARNING, "user `%s' account expired", user);
160                 return retval;
161         case PAM_AUTHTOK_EXPIRED:
162                 /* password expired */
163                 _pam_log(LOG_WARNING, "user `%s' password expired", user);
164                 return retval;
165         case PAM_NEW_AUTHTOK_REQD:
166                 /* password expired */
167                 _pam_log(LOG_WARNING, "user `%s' new password required", user);
168                 return retval;
169         case PAM_USER_UNKNOWN:
170                 /* the user does not exist */
171                 if (ctrl & WINBIND_DEBUG_ARG)
172                         _pam_log(LOG_NOTICE, "user `%s' not found",
173                                  user);
174                 if (ctrl & WINBIND_UNKNOWN_OK_ARG) {
175                         return PAM_IGNORE;
176                 }        
177                 return retval;
178         case PAM_SUCCESS:
179                 if (req_type == WINBINDD_PAM_AUTH) {
180                         /* Otherwise, the authentication looked good */
181                         _pam_log(LOG_NOTICE, "user '%s' granted access", user);
182                 } else if (req_type == WINBINDD_PAM_CHAUTHTOK) {
183                         /* Otherwise, the authentication looked good */
184                         _pam_log(LOG_NOTICE, "user '%s' password changed", user);
185                 } else { 
186                         /* Otherwise, the authentication looked good */
187                         _pam_log(LOG_NOTICE, "user '%s' OK", user);
188                 }
189                 return retval;
190         default:
191                 /* we don't know anything about this return value */
192                 _pam_log(LOG_ERR, "internal module error (retval = %d, user = `%s'",
193                          retval, user);
194                 return retval;
195         }
196 }
197
198 /* talk to winbindd */
199 static int winbind_auth_request(const char *user, const char *pass, const char *member, int ctrl)
200 {
201         struct winbindd_request request;
202         struct winbindd_response response;
203
204         ZERO_STRUCT(request);
205
206         strncpy(request.data.auth.user, user, 
207                 sizeof(request.data.auth.user)-1);
208
209         strncpy(request.data.auth.pass, pass, 
210                 sizeof(request.data.auth.pass)-1);
211
212         if (member == NULL )
213                 return pam_winbind_request_log(WINBINDD_PAM_AUTH, &request, &response, ctrl, user);
214
215         /* lookup name? */ 
216         if (!strncmp("S-", member, 2) == 0) {
217                 
218                 struct winbindd_request sid_request;
219                 struct winbindd_response sid_response;
220
221                 ZERO_STRUCT(sid_request);
222                 ZERO_STRUCT(sid_response);
223
224                 if (ctrl & WINBIND_DEBUG_ARG)
225                         _pam_log(LOG_DEBUG, "no sid given, looking up: %s\n", member);
226
227                 /* fortunatly winbindd can handle non-separated names */
228                 strcpy(sid_request.data.name.name, member);
229
230                 if (pam_winbind_request_log(WINBINDD_LOOKUPNAME, &sid_request, &sid_response, ctrl, user)) {
231                         _pam_log(LOG_INFO, "could not lookup name: %s\n", member); 
232                         return PAM_AUTH_ERR;
233                 }
234
235                 member = sid_response.data.sid.sid;
236         }
237
238         strncpy(request.data.auth.require_membership_of_sid, member, 
239                 sizeof(request.data.auth.require_membership_of_sid)-1);
240         
241         return pam_winbind_request_log(WINBINDD_PAM_AUTH, &request, &response, ctrl, user);
242 }
243
244 /* talk to winbindd */
245 static int winbind_chauthtok_request(const char *user, const char *oldpass,
246                                      const char *newpass, int ctrl)
247 {
248         struct winbindd_request request;
249         struct winbindd_response response;
250
251         ZERO_STRUCT(request);
252
253         if (request.data.chauthtok.user == NULL) return -2;
254
255         strncpy(request.data.chauthtok.user, user, 
256                 sizeof(request.data.chauthtok.user) - 1);
257
258         if (oldpass != NULL) {
259             strncpy(request.data.chauthtok.oldpass, oldpass, 
260                     sizeof(request.data.chauthtok.oldpass) - 1);
261         } else {
262             request.data.chauthtok.oldpass[0] = '\0';
263         }
264         
265         if (newpass != NULL) {
266             strncpy(request.data.chauthtok.newpass, newpass, 
267                     sizeof(request.data.chauthtok.newpass) - 1);
268         } else {
269             request.data.chauthtok.newpass[0] = '\0';
270         }
271         
272         return pam_winbind_request_log(WINBINDD_PAM_CHAUTHTOK, &request, &response, ctrl, user);
273 }
274
275 /*
276  * Checks if a user has an account
277  *
278  * return values:
279  *       1  = User not found
280  *       0  = OK
281  *      -1  = System error
282  */
283 static int valid_user(const char *user)
284 {
285         if (getpwnam(user)) return 0;
286         return 1;
287 }
288
289 static char *_pam_delete(register char *xx)
290 {
291     _pam_overwrite(xx);
292     _pam_drop(xx);
293     return NULL;
294 }
295
296 /*
297  * obtain a password from the user
298  */
299
300 static int _winbind_read_password(pam_handle_t * pamh
301                                   ,unsigned int ctrl
302                                   ,const char *comment
303                                   ,const char *prompt1
304                                   ,const char *prompt2
305                                   ,const char **pass)
306 {
307         int authtok_flag;
308         int retval;
309         const char *item;
310         char *token;
311
312         /*
313          * make sure nothing inappropriate gets returned
314          */
315
316         *pass = token = NULL;
317
318         /*
319          * which authentication token are we getting?
320          */
321
322         authtok_flag = on(WINBIND__OLD_PASSWORD, ctrl) ? PAM_OLDAUTHTOK : PAM_AUTHTOK;
323
324         /*
325          * should we obtain the password from a PAM item ?
326          */
327
328         if (on(WINBIND_TRY_FIRST_PASS_ARG, ctrl) || on(WINBIND_USE_FIRST_PASS_ARG, ctrl)) {
329                 retval = pam_get_item(pamh, authtok_flag, (const void **) &item);
330                 if (retval != PAM_SUCCESS) {
331                         /* very strange. */
332                         _pam_log(LOG_ALERT, 
333                                  "pam_get_item returned error to unix-read-password"
334                             );
335                         return retval;
336                 } else if (item != NULL) {      /* we have a password! */
337                         *pass = item;
338                         item = NULL;
339                         return PAM_SUCCESS;
340                 } else if (on(WINBIND_USE_FIRST_PASS_ARG, ctrl)) {
341                         return PAM_AUTHTOK_RECOVER_ERR;         /* didn't work */
342                 } else if (on(WINBIND_USE_AUTHTOK_ARG, ctrl)
343                            && off(WINBIND__OLD_PASSWORD, ctrl)) {
344                         return PAM_AUTHTOK_RECOVER_ERR;
345                 }
346         }
347         /*
348          * getting here implies we will have to get the password from the
349          * user directly.
350          */
351
352         {
353                 struct pam_message msg[3], *pmsg[3];
354                 struct pam_response *resp;
355                 int i, replies;
356
357                 /* prepare to converse */
358
359                 if (comment != NULL) {
360                         pmsg[0] = &msg[0];
361                         msg[0].msg_style = PAM_TEXT_INFO;
362                         msg[0].msg = comment;
363                         i = 1;
364                 } else {
365                         i = 0;
366                 }
367
368                 pmsg[i] = &msg[i];
369                 msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
370                 msg[i++].msg = prompt1;
371                 replies = 1;
372
373                 if (prompt2 != NULL) {
374                         pmsg[i] = &msg[i];
375                         msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
376                         msg[i++].msg = prompt2;
377                         ++replies;
378                 }
379                 /* so call the conversation expecting i responses */
380                 resp = NULL;
381                 retval = converse(pamh, i, pmsg, &resp);
382
383                 if (resp != NULL) {
384
385                         /* interpret the response */
386
387                         if (retval == PAM_SUCCESS) {    /* a good conversation */
388
389                                 token = x_strdup(resp[i - replies].resp);
390                                 if (token != NULL) {
391                                         if (replies == 2) {
392
393                                                 /* verify that password entered correctly */
394                                                 if (!resp[i - 1].resp
395                                                     || strcmp(token, resp[i - 1].resp)) {
396                                                         _pam_delete(token);     /* mistyped */
397                                                         retval = PAM_AUTHTOK_RECOVER_ERR;
398                                                         _make_remark(pamh                                                                   ,PAM_ERROR_MSG, MISTYPED_PASS);
399                                                 }
400                                         }
401                                 } else {
402                                         _pam_log(LOG_NOTICE
403                                                  ,"could not recover authentication token");
404                                 }
405
406                         }
407                         /*
408                          * tidy up the conversation (resp_retcode) is ignored
409                          * -- what is it for anyway? AGM
410                          */
411
412                         _pam_drop_reply(resp, i);
413
414                 } else {
415                         retval = (retval == PAM_SUCCESS)
416                             ? PAM_AUTHTOK_RECOVER_ERR : retval;
417                 }
418         }
419
420         if (retval != PAM_SUCCESS) {
421                 if (on(WINBIND_DEBUG_ARG, ctrl))
422                         _pam_log(LOG_DEBUG,
423                                  "unable to obtain a password");
424                 return retval;
425         }
426         /* 'token' is the entered password */
427
428         /* we store this password as an item */
429         
430         retval = pam_set_item(pamh, authtok_flag, token);
431         _pam_delete(token);     /* clean it up */
432         if (retval != PAM_SUCCESS
433             || (retval = pam_get_item(pamh, authtok_flag
434                                       ,(const void **) &item))
435             != PAM_SUCCESS) {
436                 
437                 _pam_log(LOG_CRIT, "error manipulating password");
438                 return retval;
439                 
440         }
441
442         *pass = item;
443         item = NULL;            /* break link to password */
444
445         return PAM_SUCCESS;
446 }
447
448 PAM_EXTERN
449 int pam_sm_authenticate(pam_handle_t *pamh, int flags,
450                         int argc, const char **argv)
451 {
452      const char *username;
453      const char *password;
454      const char *member = NULL;
455      int retval = PAM_AUTH_ERR;
456      int i;
457     
458      /* parse arguments */
459      int ctrl = _pam_parse(argc, argv);
460
461      /* Get the username */
462      retval = pam_get_user(pamh, &username, NULL);
463      if ((retval != PAM_SUCCESS) || (!username)) {
464         if (ctrl & WINBIND_DEBUG_ARG)
465             _pam_log(LOG_DEBUG,"can not get the username");
466         return PAM_SERVICE_ERR;
467      }
468      
469      retval = _winbind_read_password(pamh, ctrl, NULL, 
470                                      "Password: ", NULL,
471                                      &password);
472      
473      if (retval != PAM_SUCCESS) {
474          _pam_log(LOG_ERR, "Could not retrieve user's password");
475          return PAM_AUTHTOK_ERR;
476      }
477      
478      if (ctrl & WINBIND_DEBUG_ARG) {
479
480              /* Let's not give too much away in the log file */
481
482 #ifdef DEBUG_PASSWORD
483          _pam_log(LOG_INFO, "Verify user `%s' with password `%s'",
484                   username, password);
485 #else
486          _pam_log(LOG_INFO, "Verify user `%s'", username);
487 #endif
488      }
489
490      /* Retrieve membership-string here */
491      for ( i=0; i<argc; i++ ) {
492
493          if ((strncmp(argv[i], "require_membership_of", strlen("require_membership_of")) == 0) 
494              || (strncmp(argv[i], "require-membership-of", strlen("require-membership-of")) == 0)) {
495
496              char *p;
497              char *parm = strdup(argv[i]);
498
499              if ( (p = strchr( parm, '=' )) == NULL) {
500                 _pam_log(LOG_INFO, "no \"=\" delimiter for \"require_membership_of\" found\n");
501                 break;
502              }
503
504              member = strdup(p+1);
505          }
506      }
507
508      /* Now use the username to look up password */
509      return winbind_auth_request(username, password, member, ctrl);
510 }
511
512 PAM_EXTERN
513 int pam_sm_setcred(pam_handle_t *pamh, int flags,
514                    int argc, const char **argv)
515 {
516     return PAM_SUCCESS;
517 }
518
519 /*
520  * Account management. We want to verify that the account exists 
521  * before returning PAM_SUCCESS
522  */
523 PAM_EXTERN
524 int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
525                    int argc, const char **argv)
526 {
527     const char *username;
528     int retval = PAM_USER_UNKNOWN;
529
530     /* parse arguments */
531     int ctrl = _pam_parse(argc, argv);
532
533     /* Get the username */
534     retval = pam_get_user(pamh, &username, NULL);
535     if ((retval != PAM_SUCCESS) || (!username)) {
536         if (ctrl & WINBIND_DEBUG_ARG)
537             _pam_log(LOG_DEBUG,"can not get the username");
538         return PAM_SERVICE_ERR;
539     }
540
541     /* Verify the username */
542     retval = valid_user(username);
543     switch (retval) {
544         case -1:
545             /* some sort of system error. The log was already printed */
546             return PAM_SERVICE_ERR;
547         case 1:
548             /* the user does not exist */
549             if (ctrl & WINBIND_DEBUG_ARG)
550                 _pam_log(LOG_NOTICE, "user `%s' not found",
551                          username);
552             if (ctrl & WINBIND_UNKNOWN_OK_ARG)
553                 return PAM_IGNORE;
554             return PAM_USER_UNKNOWN;
555         case 0:
556             /* Otherwise, the authentication looked good */
557             _pam_log(LOG_NOTICE, "user '%s' granted access", username);
558             return PAM_SUCCESS;
559         default:
560             /* we don't know anything about this return value */
561             _pam_log(LOG_ERR, "internal module error (retval = %d, user = `%s'",
562                      retval, username);
563             return PAM_SERVICE_ERR;
564     }
565     
566     /* should not be reached */
567     return PAM_IGNORE;
568 }
569 PAM_EXTERN
570 int pam_sm_open_session(pam_handle_t *pamh, int flags,
571                 int argc, const char **argv)
572 {
573         /* parse arguments */
574         int ctrl = _pam_parse(argc, argv);
575         if (ctrl & WINBIND_DEBUG_ARG)
576               _pam_log(LOG_DEBUG,"libpam_winbind:pam_sm_open_session handler");
577         return PAM_SUCCESS;
578 }
579 PAM_EXTERN
580 int pam_sm_close_session(pam_handle_t *pamh, int flags,
581                 int argc, const char **argv)
582 {
583         /* parse arguments */
584         int ctrl = _pam_parse(argc, argv);
585         if (ctrl & WINBIND_DEBUG_ARG)
586               _pam_log(LOG_DEBUG,"libpam_winbind:pam_sm_close_session handler");
587         return PAM_SUCCESS;
588 }
589
590
591
592 PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
593                                 int argc, const char **argv)
594 {
595         unsigned int lctrl;
596         int retval;
597         unsigned int ctrl = _pam_parse(argc, argv);
598
599         /* <DO NOT free() THESE> */
600         const char *user;
601         const char *member = NULL;
602         char *pass_old, *pass_new;
603         /* </DO NOT free() THESE> */
604
605         char *Announce;
606         
607         int retry = 0;
608
609         /*
610          * First get the name of a user
611          */
612         retval = pam_get_user(pamh, &user, "Username: ");
613         if (retval == PAM_SUCCESS) {
614                 if (user == NULL) {
615                         _pam_log(LOG_ERR, "username was NULL!");
616                         return PAM_USER_UNKNOWN;
617                 }
618                 if (retval == PAM_SUCCESS && on(WINBIND_DEBUG_ARG, ctrl))
619                         _pam_log(LOG_DEBUG, "username [%s] obtained",
620                                  user);
621         } else {
622                 if (on(WINBIND_DEBUG_ARG, ctrl))
623                         _pam_log(LOG_DEBUG,
624                                  "password - could not identify user");
625                 return retval;
626         }
627
628         /*
629          * obtain and verify the current password (OLDAUTHTOK) for
630          * the user.
631          */
632
633         if (flags & PAM_PRELIM_CHECK) {
634                 
635                 /* instruct user what is happening */
636 #define greeting "Changing password for "
637                 Announce = (char *) malloc(sizeof(greeting) + strlen(user));
638                 if (Announce == NULL) {
639                 _pam_log(LOG_CRIT, 
640                          "password - out of memory");
641                 return PAM_BUF_ERR;
642                 }
643                 (void) strcpy(Announce, greeting);
644                 (void) strcpy(Announce + sizeof(greeting) - 1, user);
645 #undef greeting
646                 
647                 lctrl = ctrl | WINBIND__OLD_PASSWORD;
648                 retval = _winbind_read_password(pamh, lctrl
649                                                 ,Announce
650                                                 ,"(current) NT password: "
651                                                 ,NULL
652                                                 ,(const char **) &pass_old);
653                 free(Announce);
654                 
655                 if (retval != PAM_SUCCESS) {
656                         _pam_log(LOG_NOTICE
657                                  ,"password - (old) token not obtained");
658                         return retval;
659                 }
660                 /* verify that this is the password for this user */
661                 
662                 retval = winbind_auth_request(user, pass_old, member, ctrl);
663                 
664                 if (retval != PAM_ACCT_EXPIRED 
665                     && retval != PAM_AUTHTOK_EXPIRED
666                     && retval != PAM_NEW_AUTHTOK_REQD 
667                     && retval != PAM_SUCCESS) {
668                         pass_old = NULL;
669                         return retval;
670                 }
671                 
672                 retval = pam_set_item(pamh, PAM_OLDAUTHTOK, (const void *) pass_old);
673                 pass_old = NULL;
674                 if (retval != PAM_SUCCESS) {
675                         _pam_log(LOG_CRIT, 
676                                  "failed to set PAM_OLDAUTHTOK");
677                 }
678         } else if (flags & PAM_UPDATE_AUTHTOK) {
679         
680                 /*
681                  * obtain the proposed password
682                  */
683                 
684                 /*
685                  * get the old token back. 
686                  */
687                 
688                 retval = pam_get_item(pamh, PAM_OLDAUTHTOK
689                                       ,(const void **) &pass_old);
690                 
691                 if (retval != PAM_SUCCESS) {
692                         _pam_log(LOG_NOTICE, "user not authenticated");
693                         return retval;
694                 }
695                 
696                 lctrl = ctrl;
697                 
698                 if (on(WINBIND_USE_AUTHTOK_ARG, lctrl)) {
699                         ctrl = WINBIND_USE_FIRST_PASS_ARG | lctrl;
700                 }
701                 retry = 0;
702                 retval = PAM_AUTHTOK_ERR;
703                 while ((retval != PAM_SUCCESS) && (retry++ < MAX_PASSWD_TRIES)) {
704                         /*
705                          * use_authtok is to force the use of a previously entered
706                          * password -- needed for pluggable password strength checking
707                          */
708                         
709                         retval = _winbind_read_password(pamh, lctrl
710                                                         ,NULL
711                                                         ,"Enter new NT password: "
712                                                         ,"Retype new NT password: "
713                                                         ,(const char **) &pass_new);
714                         
715                         if (retval != PAM_SUCCESS) {
716                                 if (on(WINBIND_DEBUG_ARG, ctrl)) {
717                                         _pam_log(LOG_ALERT
718                                                  ,"password - new password not obtained");
719                                 }
720                                 pass_old = NULL;/* tidy up */
721                                 return retval;
722                         }
723
724                         /*
725                          * At this point we know who the user is and what they
726                          * propose as their new password. Verify that the new
727                          * password is acceptable.
728                          */
729                         
730                         if (pass_new[0] == '\0') {/* "\0" password = NULL */
731                                 pass_new = NULL;
732                         }
733                 }
734                 
735                 /*
736                  * By reaching here we have approved the passwords and must now
737                  * rebuild the password database file.
738                  */
739
740                 retval = winbind_chauthtok_request(user, pass_old, pass_new, ctrl);
741                 _pam_overwrite(pass_new);
742                 _pam_overwrite(pass_old);
743                 pass_old = pass_new = NULL;
744         } else {
745                 retval = PAM_SERVICE_ERR;
746         }
747         
748         return retval;
749 }
750
751 #ifdef PAM_STATIC
752
753 /* static module data */
754
755 struct pam_module _pam_winbind_modstruct = {
756      MODULE_NAME,
757      pam_sm_authenticate,
758      pam_sm_setcred,
759      pam_sm_acct_mgmt,
760      pam_sm_open_session,
761      pam_sm_close_session,
762      pam_sm_chauthtok
763 };
764
765 #endif
766
767 /*
768  * Copyright (c) Andrew Tridgell  <tridge@samba.org>   2000
769  * Copyright (c) Tim Potter       <tpot@samba.org>     2000
770  * Copyright (c) Andrew Bartlettt <abartlet@samba.org> 2002
771  * Copyright (c) Jan Rêkorajski 1999.
772  * Copyright (c) Andrew G. Morgan 1996-8.
773  * Copyright (c) Alex O. Yuriev, 1996.
774  * Copyright (c) Cristian Gafton 1996.
775  * Copyright (C) Elliot Lee <sopwith@redhat.com> 1996, Red Hat Software. 
776  *
777  * Redistribution and use in source and binary forms, with or without
778  * modification, are permitted provided that the following conditions
779  * are met:
780  * 1. Redistributions of source code must retain the above copyright
781  *    notice, and the entire permission notice in its entirety,
782  *    including the disclaimer of warranties.
783  * 2. Redistributions in binary form must reproduce the above copyright
784  *    notice, this list of conditions and the following disclaimer in the
785  *    documentation and/or other materials provided with the distribution.
786  * 3. The name of the author may not be used to endorse or promote
787  *    products derived from this software without specific prior
788  *    written permission.
789  *
790  * ALTERNATIVELY, this product may be distributed under the terms of
791  * the GNU Public License, in which case the provisions of the GPL are
792  * required INSTEAD OF the above restrictions.  (This clause is
793  * necessary due to a potential bad interaction between the GPL and
794  * the restrictions contained in a BSD-style copyright.)
795  *
796  * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED
797  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
798  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
799  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
800  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
801  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
802  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
803  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
804  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
805  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
806  * OF THE POSSIBILITY OF SUCH DAMAGE.
807  */