Removed 'extern int DEBUGLEVEL' as it is now in the smb.h header.
[samba.git] / source3 / auth / pampass.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 2.2.
4    PAM Password checking
5    Copyright (C) Andrew Tridgell 1992-2001
6    Copyright (C) John H Terpsta 1999-2001
7    Copyright (C) Andrew Bartlett 2001
8    Copyright (C) Jeremy Allison 2001
9    
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 2 of the License, or
13    (at your option) any later version.
14    
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19    
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 */
24
25 /*
26  * This module provides PAM based functions for validation of
27  * username/password pairs, account managment, session and access control.
28  * Note: SMB password checking is done in smbpass.c
29  */
30
31 #include "includes.h"
32
33 #ifdef WITH_PAM
34
35 /*******************************************************************
36  * Handle PAM authentication 
37  *      - Access, Authentication, Session, Password
38  *   Note: See PAM Documentation and refer to local system PAM implementation
39  *   which determines what actions/limitations/allowances become affected.
40  *********************************************************************/
41
42 #include <security/pam_appl.h>
43
44 /*
45  * Structure used to communicate between the conversation function
46  * and the server_login/change password functions.
47  */
48
49 struct smb_pam_userdata {
50         char *PAM_username;
51         char *PAM_password;
52         char *PAM_newpassword;
53 };
54
55 typedef int (*smb_pam_conv_fn)(int, const struct pam_message **, struct pam_response **, void *appdata_ptr);
56
57 /*
58  *  Macros to help make life easy
59  */
60 #define COPY_STRING(s) (s) ? strdup(s) : NULL
61
62 /*******************************************************************
63  PAM error handler.
64  *********************************************************************/
65
66 static BOOL smb_pam_error_handler(pam_handle_t *pamh, int pam_error, char *msg, int dbglvl)
67 {
68
69         if( pam_error != PAM_SUCCESS) {
70                 DEBUG(dbglvl, ("smb_pam_error_handler: PAM: %s : %s\n",
71                                 msg, pam_strerror(pamh, pam_error)));
72                 
73                 return False;
74         }
75         return True;
76 }
77
78 /*******************************************************************
79  This function is a sanity check, to make sure that we NEVER report
80  failure as sucess.
81 *********************************************************************/
82
83 static BOOL smb_pam_nt_status_error_handler(pam_handle_t *pamh, int pam_error,
84                                                         char *msg, int dbglvl, 
85                                             NTSTATUS *nt_status)
86 {
87         if (smb_pam_error_handler(pamh, pam_error, msg, dbglvl))
88                 return True;
89
90         if (NT_STATUS_IS_OK(*nt_status)) {
91                 /* Complain LOUDLY */
92                 DEBUG(0, ("smb_pam_nt_status_error_handler: PAM: BUG: PAM and NT_STATUS \
93 error MISMATCH, forcing to NT_STATUS_LOGON_FAILURE"));
94                 *nt_status = NT_STATUS_LOGON_FAILURE;
95         }
96         return False;
97 }
98
99 /*
100  * PAM conversation function
101  * Here we assume (for now, at least) that echo on means login name, and
102  * echo off means password.
103  */
104
105 static int smb_pam_conv(int num_msg,
106                     const struct pam_message **msg,
107                     struct pam_response **resp,
108                     void *appdata_ptr)
109 {
110         int replies = 0;
111         struct pam_response *reply = NULL;
112         struct smb_pam_userdata *udp = (struct smb_pam_userdata *)appdata_ptr;
113
114         *resp = NULL;
115
116         if (num_msg <= 0)
117                 return PAM_CONV_ERR;
118
119         /*
120          * Apparantly HPUX has a buggy PAM that doesn't support the
121          * appdata_ptr. Fail if this is the case. JRA.
122          */
123
124         if (udp == NULL) {
125                 DEBUG(0,("smb_pam_conv: PAM on this system is broken - appdata_ptr == NULL !\n"));
126                 return PAM_CONV_ERR;
127         }
128
129         reply = malloc(sizeof(struct pam_response) * num_msg);
130         if (!reply)
131                 return PAM_CONV_ERR;
132
133         memset(reply, '\0', sizeof(struct pam_response) * num_msg);
134
135         for (replies = 0; replies < num_msg; replies++) {
136                 switch (msg[replies]->msg_style) {
137                         case PAM_PROMPT_ECHO_ON:
138                                 reply[replies].resp_retcode = PAM_SUCCESS;
139                                 reply[replies].resp = COPY_STRING(udp->PAM_username);
140                                 /* PAM frees resp */
141                                 break;
142
143                         case PAM_PROMPT_ECHO_OFF:
144                                 reply[replies].resp_retcode = PAM_SUCCESS;
145                                 reply[replies].resp = COPY_STRING(udp->PAM_password);
146                                 /* PAM frees resp */
147                                 break;
148
149                         case PAM_TEXT_INFO:
150                                 /* fall through */
151
152                         case PAM_ERROR_MSG:
153                                 /* ignore it... */
154                                 reply[replies].resp_retcode = PAM_SUCCESS;
155                                 reply[replies].resp = NULL;
156                                 break;
157
158                         default:
159                                 /* Must be an error of some sort... */
160                                 SAFE_FREE(reply);
161                                 return PAM_CONV_ERR;
162                 }
163         }
164         if (reply)
165                 *resp = reply;
166         return PAM_SUCCESS;
167 }
168
169 /*
170  * PAM password change conversation function
171  * Here we assume (for now, at least) that echo on means login name, and
172  * echo off means password.
173  */
174
175 static void special_char_sub(char *buf)
176 {
177         all_string_sub(buf, "\\n", "", 0);
178         all_string_sub(buf, "\\r", "", 0);
179         all_string_sub(buf, "\\s", " ", 0);
180         all_string_sub(buf, "\\t", "\t", 0);
181 }
182
183 static void pwd_sub(char *buf, char *username, char *oldpass, char *newpass)
184 {
185         pstring_sub(buf, "%u", username);
186         all_string_sub(buf, "%o", oldpass, sizeof(fstring));
187         all_string_sub(buf, "%n", newpass, sizeof(fstring));
188 }
189
190
191 struct chat_struct {
192         struct chat_struct *next, *prev;
193         fstring prompt;
194         fstring reply;
195 };
196
197 /**************************************************************
198  Create a linked list containing chat data.
199 ***************************************************************/
200
201 static struct chat_struct *make_pw_chat(char *p) 
202 {
203         fstring prompt;
204         fstring reply;
205         struct chat_struct *list = NULL;
206         struct chat_struct *t;
207         struct chat_struct *tmp;
208
209         while (1) {
210                 t = (struct chat_struct *)malloc(sizeof(*t));
211                 if (!t) {
212                         DEBUG(0,("make_pw_chat: malloc failed!\n"));
213                         return NULL;
214                 }
215
216                 ZERO_STRUCTP(t);
217
218                 DLIST_ADD_END(list, t, tmp);
219
220                 if (!next_token(&p, prompt, NULL, sizeof(fstring)))
221                         break;
222
223                 if (strequal(prompt,"."))
224                         fstrcpy(prompt,"*");
225
226                 special_char_sub(prompt);
227                 fstrcpy(t->prompt, prompt);
228                 strlower(t->prompt);
229                 trim_string(t->prompt, " ", " ");
230
231                 if (!next_token(&p, reply, NULL, sizeof(fstring)))
232                         break;
233
234                 if (strequal(reply,"."))
235                                 fstrcpy(reply,"");
236
237                 special_char_sub(reply);
238                 fstrcpy(t->reply, reply);
239                 strlower(t->reply);
240                 trim_string(t->reply, " ", " ");
241
242         }
243         return list;
244 }
245
246 static void free_pw_chat(struct chat_struct *list)
247 {
248     while (list) {
249         struct chat_struct *old_head = list;
250         DLIST_REMOVE(list, list);
251         SAFE_FREE(old_head);
252     }
253 }
254
255 static int smb_pam_passchange_conv(int num_msg,
256                                 const struct pam_message **msg,
257                                 struct pam_response **resp,
258                                 void *appdata_ptr)
259 {
260         int replies = 0;
261         struct pam_response *reply = NULL;
262         fstring current_prompt;
263         fstring current_reply;
264         struct smb_pam_userdata *udp = (struct smb_pam_userdata *)appdata_ptr;
265         struct chat_struct *pw_chat= make_pw_chat(lp_passwd_chat());
266         struct chat_struct *t;
267         BOOL found; 
268         *resp = NULL;
269         
270         DEBUG(10,("smb_pam_passchange_conv: starting converstation for %d messages\n", num_msg));
271
272         if (num_msg <= 0)
273                 return PAM_CONV_ERR;
274
275         if (pw_chat == NULL)
276                 return PAM_CONV_ERR;
277
278         /*
279          * Apparantly HPUX has a buggy PAM that doesn't support the
280          * appdata_ptr. Fail if this is the case. JRA.
281          */
282
283         if (udp == NULL) {
284                 DEBUG(0,("smb_pam_passchange_conv: PAM on this system is broken - appdata_ptr == NULL !\n"));
285                 free_pw_chat(pw_chat);
286                 return PAM_CONV_ERR;
287         }
288
289         reply = malloc(sizeof(struct pam_response) * num_msg);
290         if (!reply) {
291                 DEBUG(0,("smb_pam_passchange_conv: malloc for reply failed!\n"));
292                 free_pw_chat(pw_chat);
293                 return PAM_CONV_ERR;
294         }
295
296         for (replies = 0; replies < num_msg; replies++) {
297                 found = False;
298                 DEBUG(10,("smb_pam_passchange_conv: Processing message %d\n", replies));
299                 switch (msg[replies]->msg_style) {
300                 case PAM_PROMPT_ECHO_ON:
301                         DEBUG(10,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_ON: PAM said: %s\n", msg[replies]->msg));
302                         fstrcpy(current_prompt, msg[replies]->msg);
303                         trim_string(current_prompt, " ", " ");
304                         for (t=pw_chat; t; t=t->next) {
305
306                                 DEBUG(10,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_ON: trying to match |%s| to |%s|\n",
307                                                 t->prompt, current_prompt ));
308
309                                 if (wild_match(t->prompt, current_prompt) == 0) {
310                                         fstrcpy(current_reply, t->reply);
311                                         DEBUG(10,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_ON: We sent: %s\n", current_reply));
312                                         pwd_sub(current_reply, udp->PAM_username, udp->PAM_password, udp->PAM_newpassword);
313 #ifdef DEBUG_PASSWORD
314                                         DEBUG(100,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_ON: We actualy sent: %s\n", current_reply));
315 #endif
316                                         reply[replies].resp_retcode = PAM_SUCCESS;
317                                         reply[replies].resp = COPY_STRING(current_reply);
318                                         found = True;
319                                         break;
320                                 }
321                         }
322                         /* PAM frees resp */
323                         if (!found) {
324                                 DEBUG(3,("smb_pam_passchange_conv: Could not find reply for PAM prompt: %s\n",msg[replies]->msg));
325                                 free_pw_chat(pw_chat);
326                                 SAFE_FREE(reply);
327                                 return PAM_CONV_ERR;
328                         }
329                         break;
330
331                 case PAM_PROMPT_ECHO_OFF:
332                         DEBUG(10,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_OFF: PAM said: %s\n", msg[replies]->msg));
333                         fstrcpy(current_prompt, msg[replies]->msg);
334                         trim_string(current_prompt, " ", " ");
335                         for (t=pw_chat; t; t=t->next) {
336
337                                 DEBUG(10,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_OFF: trying to match |%s| to |%s|\n",
338                                                 t->prompt, current_prompt ));
339
340                                 if (wild_match(t->prompt, current_prompt) == 0) {
341                                         fstrcpy(current_reply, t->reply);
342                                         DEBUG(10,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_OFF: We sent: %s\n", current_reply));
343                                         pwd_sub(current_reply, udp->PAM_username, udp->PAM_password, udp->PAM_newpassword);
344                                         reply[replies].resp_retcode = PAM_SUCCESS;
345                                         reply[replies].resp = COPY_STRING(current_reply);
346 #ifdef DEBUG_PASSWORD
347                                         DEBUG(100,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_OFF: We actualy sent: %s\n", current_reply));
348 #endif
349                                         found = True;
350                                         break;
351                                 }
352                         }
353                         /* PAM frees resp */
354                         
355                         if (!found) {
356                                 DEBUG(3,("smb_pam_passchange_conv: Could not find reply for PAM prompt: %s\n",msg[replies]->msg));
357                                 free_pw_chat(pw_chat);
358                                 SAFE_FREE(reply);
359                                 return PAM_CONV_ERR;
360                         }
361                         break;
362
363                 case PAM_TEXT_INFO:
364                         /* fall through */
365
366                 case PAM_ERROR_MSG:
367                         /* ignore it... */
368                         reply[replies].resp_retcode = PAM_SUCCESS;
369                         reply[replies].resp = NULL;
370                         break;
371                         
372                 default:
373                         /* Must be an error of some sort... */
374                         free_pw_chat(pw_chat);
375                         SAFE_FREE(reply);
376                         return PAM_CONV_ERR;
377                 }
378         }
379                 
380         free_pw_chat(pw_chat);
381         if (reply)
382                 *resp = reply;
383         return PAM_SUCCESS;
384 }
385
386 /***************************************************************************
387  Free up a malloced pam_conv struct.
388 ****************************************************************************/
389
390 static void smb_free_pam_conv(struct pam_conv *pconv)
391 {
392         if (pconv)
393                 SAFE_FREE(pconv->appdata_ptr);
394
395         SAFE_FREE(pconv);
396 }
397
398 /***************************************************************************
399  Allocate a pam_conv struct.
400 ****************************************************************************/
401
402 static struct pam_conv *smb_setup_pam_conv(smb_pam_conv_fn smb_pam_conv_fnptr, char *user,
403                                         char *passwd, char *newpass)
404 {
405         struct pam_conv *pconv = (struct pam_conv *)malloc(sizeof(struct pam_conv));
406         struct smb_pam_userdata *udp = (struct smb_pam_userdata *)malloc(sizeof(struct smb_pam_userdata));
407
408         if (pconv == NULL || udp == NULL) {
409                 SAFE_FREE(pconv);
410                 SAFE_FREE(udp);
411                 return NULL;
412         }
413
414         udp->PAM_username = user;
415         udp->PAM_password = passwd;
416         udp->PAM_newpassword = newpass;
417
418         pconv->conv = smb_pam_conv_fnptr;
419         pconv->appdata_ptr = (void *)udp;
420         return pconv;
421 }
422
423 /* 
424  * PAM Closing out cleanup handler
425  */
426
427 static BOOL smb_pam_end(pam_handle_t *pamh, struct pam_conv *smb_pam_conv_ptr)
428 {
429         int pam_error;
430
431         smb_free_pam_conv(smb_pam_conv_ptr);
432        
433         if( pamh != NULL ) {
434                 pam_error = pam_end(pamh, 0);
435                 if(smb_pam_error_handler(pamh, pam_error, "End Cleanup Failed", 2) == True) {
436                         DEBUG(4, ("smb_pam_end: PAM: PAM_END OK.\n"));
437                         return True;
438                 }
439         }
440         DEBUG(2,("smb_pam_end: PAM: not initialised"));
441         return False;
442 }
443
444 /*
445  * Start PAM authentication for specified account
446  */
447
448 static BOOL smb_pam_start(pam_handle_t **pamh, char *user, char *rhost, struct pam_conv *pconv)
449 {
450         int pam_error;
451
452         *pamh = (pam_handle_t *)NULL;
453
454         DEBUG(4,("smb_pam_start: PAM: Init user: %s\n", user));
455
456         pam_error = pam_start("samba", user, pconv, pamh);
457         if( !smb_pam_error_handler(*pamh, pam_error, "Init Failed", 0)) {
458                 *pamh = (pam_handle_t *)NULL;
459                 return False;
460         }
461
462         if (rhost == NULL) {
463                 rhost = client_name();
464                 if (strequal(rhost,"UNKNOWN"))
465                         rhost = client_addr();
466         }
467
468 #ifdef PAM_RHOST
469         DEBUG(4,("smb_pam_start: PAM: setting rhost to: %s\n", rhost));
470         pam_error = pam_set_item(*pamh, PAM_RHOST, rhost);
471         if(!smb_pam_error_handler(*pamh, pam_error, "set rhost failed", 0)) {
472                 smb_pam_end(*pamh, pconv);
473                 *pamh = (pam_handle_t *)NULL;
474                 return False;
475         }
476 #endif
477 #ifdef PAM_TTY
478         DEBUG(4,("smb_pam_start: PAM: setting tty\n"));
479         pam_error = pam_set_item(*pamh, PAM_TTY, "samba");
480         if (!smb_pam_error_handler(*pamh, pam_error, "set tty failed", 0)) {
481                 smb_pam_end(*pamh, pconv);
482                 *pamh = (pam_handle_t *)NULL;
483                 return False;
484         }
485 #endif
486         DEBUG(4,("smb_pam_start: PAM: Init passed for user: %s\n", user));
487         return True;
488 }
489
490 /*
491  * PAM Authentication Handler
492  */
493 static NTSTATUS smb_pam_auth(pam_handle_t *pamh, char *user)
494 {
495         int pam_error;
496         NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE;
497
498         /*
499          * To enable debugging set in /etc/pam.d/samba:
500          *      auth required /lib/security/pam_pwdb.so nullok shadow audit
501          */
502         
503         DEBUG(4,("smb_pam_auth: PAM: Authenticate User: %s\n", user));
504         pam_error = pam_authenticate(pamh, PAM_SILENT | lp_null_passwords() ? 0 : PAM_DISALLOW_NULL_AUTHTOK);
505         switch( pam_error ){
506                 case PAM_AUTH_ERR:
507                         DEBUG(2, ("smb_pam_auth: PAM: Athentication Error for user %s\n", user));
508                         nt_status = NT_STATUS_WRONG_PASSWORD;
509                         break;
510                 case PAM_CRED_INSUFFICIENT:
511                         DEBUG(2, ("smb_pam_auth: PAM: Insufficient Credentials for user %s\n", user));
512                         nt_status = NT_STATUS_INSUFFICIENT_LOGON_INFO;
513                         break;
514                 case PAM_AUTHINFO_UNAVAIL:
515                         DEBUG(2, ("smb_pam_auth: PAM: Authentication Information Unavailable for user %s\n", user));
516                         nt_status = NT_STATUS_LOGON_FAILURE;
517                         break;
518                 case PAM_USER_UNKNOWN:
519                         DEBUG(2, ("smb_pam_auth: PAM: Username %s NOT known to Authentication system\n", user));
520                         nt_status = NT_STATUS_NO_SUCH_USER;
521                         break;
522                 case PAM_MAXTRIES:
523                         DEBUG(2, ("smb_pam_auth: PAM: One or more authentication modules reports user limit for user %s exceeeded\n", user));
524                         nt_status = NT_STATUS_REMOTE_SESSION_LIMIT;
525                         break;
526                 case PAM_ABORT:
527                         DEBUG(0, ("smb_pam_auth: PAM: One or more PAM modules failed to load for user %s\n", user));
528                         nt_status = NT_STATUS_LOGON_FAILURE;
529                         break;
530                 case PAM_SUCCESS:
531                         DEBUG(4, ("smb_pam_auth: PAM: User %s Authenticated OK\n", user));
532                         nt_status = NT_STATUS_OK;
533                         break;
534                 default:
535                         DEBUG(0, ("smb_pam_auth: PAM: UNKNOWN ERROR while authenticating user %s\n", user));
536                         nt_status = NT_STATUS_LOGON_FAILURE;
537                         break;
538         }
539
540         smb_pam_nt_status_error_handler(pamh, pam_error, "Authentication Failure", 2, &nt_status);
541         return nt_status;
542 }
543
544 /* 
545  * PAM Account Handler
546  */
547 static NTSTATUS smb_pam_account(pam_handle_t *pamh, char * user)
548 {
549         int pam_error;
550         NTSTATUS nt_status = NT_STATUS_ACCOUNT_DISABLED;
551
552         DEBUG(4,("smb_pam_account: PAM: Account Management for User: %s\n", user));
553         pam_error = pam_acct_mgmt(pamh, PAM_SILENT); /* Is user account enabled? */
554         switch( pam_error ) {
555                 case PAM_AUTHTOK_EXPIRED:
556                         DEBUG(2, ("smb_pam_account: PAM: User %s is valid but password is expired\n", user));
557                         nt_status = NT_STATUS_PASSWORD_EXPIRED;
558                         break;
559                 case PAM_ACCT_EXPIRED:
560                         DEBUG(2, ("smb_pam_account: PAM: User %s no longer permitted to access system\n", user));
561                         nt_status = NT_STATUS_ACCOUNT_EXPIRED;
562                         break;
563                 case PAM_AUTH_ERR:
564                         DEBUG(2, ("smb_pam_account: PAM: There was an authentication error for user %s\n", user));
565                         nt_status = NT_STATUS_LOGON_FAILURE;
566                         break;
567                 case PAM_PERM_DENIED:
568                         DEBUG(0, ("smb_pam_account: PAM: User %s is NOT permitted to access system at this time\n", user));
569                         nt_status = NT_STATUS_ACCOUNT_RESTRICTION;
570                         break;
571                 case PAM_USER_UNKNOWN:
572                         DEBUG(0, ("smb_pam_account: PAM: User \"%s\" is NOT known to account management\n", user));
573                         nt_status = NT_STATUS_NO_SUCH_USER;
574                         break;
575                 case PAM_SUCCESS:
576                         DEBUG(4, ("smb_pam_account: PAM: Account OK for User: %s\n", user));
577                         nt_status = NT_STATUS_OK;
578                         break;
579                 default:
580                         nt_status = NT_STATUS_ACCOUNT_DISABLED;
581                         DEBUG(0, ("smb_pam_account: PAM: UNKNOWN PAM ERROR (%d) during Account Management for User: %s\n", pam_error, user));
582                         break;
583         }
584
585         smb_pam_nt_status_error_handler(pamh, pam_error, "Account Check Failed", 2, &nt_status);
586         return nt_status;
587 }
588
589 /*
590  * PAM Credential Setting
591  */
592
593 static NTSTATUS smb_pam_setcred(pam_handle_t *pamh, char * user)
594 {
595         int pam_error;
596         NTSTATUS nt_status = NT_STATUS_NO_TOKEN;
597
598         /*
599          * This will allow samba to aquire a kerberos token. And, when
600          * exporting an AFS cell, be able to /write/ to this cell.
601          */
602
603         DEBUG(4,("PAM: Account Management SetCredentials for User: %s\n", user));
604         pam_error = pam_setcred(pamh, (PAM_ESTABLISH_CRED|PAM_SILENT)); 
605         switch( pam_error ) {
606                 case PAM_CRED_UNAVAIL:
607                         DEBUG(0, ("smb_pam_setcred: PAM: Credentials not found for user:%s\n", user ));
608                          nt_status = NT_STATUS_NO_TOKEN;
609                         break;
610                 case PAM_CRED_EXPIRED:
611                         DEBUG(0, ("smb_pam_setcred: PAM: Credentials for user: \"%s\" EXPIRED!\n", user ));
612                         nt_status = NT_STATUS_PASSWORD_EXPIRED;
613                         break;
614                 case PAM_USER_UNKNOWN:
615                         DEBUG(0, ("smb_pam_setcred: PAM: User: \"%s\" is NOT known so can not set credentials!\n", user ));
616                         nt_status = NT_STATUS_NO_SUCH_USER;
617                         break;
618                 case PAM_CRED_ERR:
619                         DEBUG(0, ("smb_pam_setcred: PAM: Unknown setcredentials error - unable to set credentials for %s\n", user ));
620                         nt_status = NT_STATUS_LOGON_FAILURE;
621                         break;
622                 case PAM_SUCCESS:
623                         DEBUG(4, ("smb_pam_setcred: PAM: SetCredentials OK for User: %s\n", user));
624                         nt_status = NT_STATUS_OK;
625                         break;
626                 default:
627                         DEBUG(0, ("smb_pam_setcred: PAM: UNKNOWN PAM ERROR (%d) during SetCredentials for User: %s\n", pam_error, user));
628                         nt_status = NT_STATUS_NO_TOKEN;
629                         break;
630         }
631
632         smb_pam_nt_status_error_handler(pamh, pam_error, "Set Credential Failure", 2, &nt_status);
633         return nt_status;
634 }
635
636 /*
637  * PAM Internal Session Handler
638  */
639 static BOOL smb_internal_pam_session(pam_handle_t *pamh, char *user, char *tty, BOOL flag)
640 {
641         int pam_error;
642
643 #ifdef PAM_TTY
644         DEBUG(4,("smb_internal_pam_session: PAM: tty set to: %s\n", tty));
645         pam_error = pam_set_item(pamh, PAM_TTY, tty);
646         if (!smb_pam_error_handler(pamh, pam_error, "set tty failed", 0))
647                 return False;
648 #endif
649
650         if (flag) {
651                 pam_error = pam_open_session(pamh, PAM_SILENT);
652                 if (!smb_pam_error_handler(pamh, pam_error, "session setup failed", 0))
653                         return False;
654         } else {
655                 pam_setcred(pamh, (PAM_DELETE_CRED|PAM_SILENT)); /* We don't care if this fails */
656                 pam_error = pam_close_session(pamh, PAM_SILENT); /* This will probably pick up the error anyway */
657                 if (!smb_pam_error_handler(pamh, pam_error, "session close failed", 0))
658                         return False;
659         }
660         return (True);
661 }
662
663 /*
664  * Internal PAM Password Changer.
665  */
666
667 static BOOL smb_pam_chauthtok(pam_handle_t *pamh, char * user)
668 {
669         int pam_error;
670
671         DEBUG(4,("smb_pam_chauthtok: PAM: Password Change for User: %s\n", user));
672
673         pam_error = pam_chauthtok(pamh, PAM_SILENT); /* Change Password */
674
675         switch( pam_error ) {
676         case PAM_AUTHTOK_ERR:
677                 DEBUG(2, ("PAM: unable to obtain the new authentication token - is password to weak?\n"));
678                 break;
679
680         /* This doesn't seem to be defined on Solaris. JRA */
681 #ifdef PAM_AUTHTOK_RECOVER_ERR
682         case PAM_AUTHTOK_RECOVER_ERR:
683                 DEBUG(2, ("PAM: unable to obtain the old authentication token - was the old password wrong?.\n"));
684                 break;
685 #endif
686
687         case PAM_AUTHTOK_LOCK_BUSY:
688                 DEBUG(2, ("PAM: unable to change the authentication token since it is currently locked.\n"));
689                 break;
690         case PAM_AUTHTOK_DISABLE_AGING:
691                 DEBUG(2, ("PAM: Authentication token aging has been disabled.\n"));
692                 break;
693         case PAM_PERM_DENIED:
694                 DEBUG(0, ("PAM: Permission denied.\n"));
695                 break;
696         case PAM_TRY_AGAIN:
697                 DEBUG(0, ("PAM: Could not update all authentication token(s). No authentication tokens were updated.\n"));
698                 break;
699         case PAM_USER_UNKNOWN:
700                 DEBUG(0, ("PAM: User not known to PAM\n"));
701                 break;
702         case PAM_SUCCESS:
703                 DEBUG(4, ("PAM: Account OK for User: %s\n", user));
704                 break;
705         default:
706                 DEBUG(0, ("PAM: UNKNOWN PAM ERROR (%d) for User: %s\n", pam_error, user));
707         }
708  
709         if(!smb_pam_error_handler(pamh, pam_error, "Password Change Failed", 2)) {
710                 return False;
711         }
712
713         /* If this point is reached, the password has changed. */
714         return True;
715 }
716
717 /*
718  * PAM Externally accessible Session handler
719  */
720
721 BOOL smb_pam_claim_session(char *user, char *tty, char *rhost)
722 {
723         pam_handle_t *pamh = NULL;
724         struct pam_conv *pconv = NULL;
725
726         /* Ignore PAM if told to. */
727
728         if (!lp_obey_pam_restrictions())
729                 return True;
730
731         if ((pconv = smb_setup_pam_conv(smb_pam_conv, user, NULL, NULL)) == NULL)
732                 return False;
733
734         if (!smb_pam_start(&pamh, user, rhost, pconv))
735                 return False;
736
737         if (!smb_internal_pam_session(pamh, user, tty, True)) {
738                 smb_pam_end(pamh, pconv);
739                 return False;
740         }
741
742         return smb_pam_end(pamh, pconv);
743 }
744
745 /*
746  * PAM Externally accessible Session handler
747  */
748
749 BOOL smb_pam_close_session(char *user, char *tty, char *rhost)
750 {
751         pam_handle_t *pamh = NULL;
752         struct pam_conv *pconv = NULL;
753
754         /* Ignore PAM if told to. */
755
756         if (!lp_obey_pam_restrictions())
757                 return True;
758
759         if ((pconv = smb_setup_pam_conv(smb_pam_conv, user, NULL, NULL)) == NULL)
760                 return False;
761
762         if (!smb_pam_start(&pamh, user, rhost, pconv))
763                 return False;
764
765         if (!smb_internal_pam_session(pamh, user, tty, False)) {
766                 smb_pam_end(pamh, pconv);
767                 return False;
768         }
769
770         return smb_pam_end(pamh, pconv);
771 }
772
773 /*
774  * PAM Externally accessible Account handler
775  */
776
777 NTSTATUS smb_pam_accountcheck(char * user)
778 {
779         NTSTATUS nt_status = NT_STATUS_ACCOUNT_DISABLED;
780         pam_handle_t *pamh = NULL;
781         struct pam_conv *pconv = NULL;
782
783         /* Ignore PAM if told to. */
784
785         if (!lp_obey_pam_restrictions())
786                 return NT_STATUS_OK;
787
788         if ((pconv = smb_setup_pam_conv(smb_pam_conv, user, NULL, NULL)) == NULL)
789                 return NT_STATUS_NO_MEMORY;
790
791         if (!smb_pam_start(&pamh, user, NULL, pconv))
792                 return NT_STATUS_ACCOUNT_DISABLED;
793
794         if (!NT_STATUS_IS_OK(nt_status = smb_pam_account(pamh, user)))
795                 DEBUG(0, ("smb_pam_accountcheck: PAM: Account Validation Failed - Rejecting User %s!\n", user));
796
797         smb_pam_end(pamh, pconv);
798         return nt_status;
799 }
800
801 /*
802  * PAM Password Validation Suite
803  */
804
805 NTSTATUS smb_pam_passcheck(char * user, char * password)
806 {
807         pam_handle_t *pamh = NULL;
808         NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE;
809         struct pam_conv *pconv = NULL;
810
811         /*
812          * Note we can't ignore PAM here as this is the only
813          * way of doing auths on plaintext passwords when
814          * compiled --with-pam.
815          */
816
817         if ((pconv = smb_setup_pam_conv(smb_pam_conv, user, password, NULL)) == NULL)
818                 return NT_STATUS_LOGON_FAILURE;
819
820         if (!smb_pam_start(&pamh, user, NULL, pconv))
821                 return NT_STATUS_LOGON_FAILURE;
822
823         if (!NT_STATUS_IS_OK(nt_status = smb_pam_auth(pamh, user))) {
824                 DEBUG(0, ("smb_pam_passcheck: PAM: smb_pam_auth failed - Rejecting User %s !\n", user));
825                 smb_pam_end(pamh, pconv);
826                 return nt_status;
827         }
828
829         if (!NT_STATUS_IS_OK(nt_status = smb_pam_account(pamh, user))) {
830                 DEBUG(0, ("smb_pam_passcheck: PAM: smb_pam_account failed - Rejecting User %s !\n", user));
831                 smb_pam_end(pamh, pconv);
832                 return nt_status;
833         }
834
835         if (!NT_STATUS_IS_OK(nt_status = smb_pam_setcred(pamh, user))) {
836                 DEBUG(0, ("smb_pam_passcheck: PAM: smb_pam_setcred failed - Rejecting User %s !\n", user));
837                 smb_pam_end(pamh, pconv);
838                 return nt_status;
839         }
840
841         smb_pam_end(pamh, pconv);
842         return nt_status;
843 }
844
845 /*
846  * PAM Password Change Suite
847  */
848
849 BOOL smb_pam_passchange(char * user, char * oldpassword, char * newpassword)
850 {
851         /* Appropriate quantities of root should be obtained BEFORE calling this function */
852         struct pam_conv *pconv = NULL;
853         pam_handle_t *pamh = NULL;
854
855         if ((pconv = smb_setup_pam_conv(smb_pam_passchange_conv, user, oldpassword, newpassword)) == NULL)
856                 return False;
857
858         if(!smb_pam_start(&pamh, user, NULL, pconv))
859                 return False;
860
861         if (!smb_pam_chauthtok(pamh, user)) {
862                 DEBUG(0, ("smb_pam_passchange: PAM: Password Change Failed for user %s!\n", user));
863                 smb_pam_end(pamh, pconv);
864                 return False;
865         }
866
867         return smb_pam_end(pamh, pconv);
868 }
869
870 #else
871
872 /* If PAM not used, no PAM restrictions on accounts. */
873  NTSTATUS smb_pam_accountcheck(char * user)
874 {
875         return NT_STATUS_OK;
876 }
877
878 /* If PAM not used, also no PAM restrictions on sessions. */
879  BOOL smb_pam_claim_session(char *user, char *tty, char *rhost)
880 {
881         return True;
882 }
883
884 /* If PAM not used, also no PAM restrictions on sessions. */
885  BOOL smb_pam_close_session(char *in_user, char *tty, char *rhost)
886 {
887         return True;
888 }
889 #endif /* WITH_PAM */