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