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