Added JohnT and Andrew Bartlett's PAM changes.
[ira/wip.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 Barton 2001
8    
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24 /*
25  * This module provides PAM based functions for validation of
26  * username/password pairs, account managment, session and access control.
27  * Note: SMB password checking is done in smbpass.c
28  */
29
30 #include "includes.h"
31
32 extern int DEBUGLEVEL;
33
34 #ifdef WITH_PAM
35
36 /*******************************************************************
37  * Handle PAM authentication 
38  *      - Access, Authentication, Session, Password
39  *   Note: See PAM Documentation and refer to local system PAM implementation
40  *   which determines what actions/limitations/allowances become affected.
41  *********************************************************************/
42
43 #include <security/pam_appl.h>
44
45 /*
46  * Static variables used to communicate between the conversation function
47  * and the server_login function
48  */
49
50 static char *PAM_username;
51 static char *PAM_password;
52
53 /*
54  *  Macros to help make life easy
55  */
56 #define COPY_STRING(s) (s) ? strdup(s) : NULL
57
58 /*
59  * Macro converted to a function to simplyify this thing
60  */
61 static BOOL pam_error_handler(pam_handle_t *pamh, int pam_error, char *msg, int dbglvl)
62 {
63
64         int retval;
65
66         if( pam_error != PAM_SUCCESS)
67         {
68                 DEBUG(dbglvl, ("PAM %s: %s\n", pam_strerror(pamh, pam_error)));
69                 return False;
70         }
71         return True;
72 }
73
74 /*
75  * PAM conversation function
76  * Here we assume (for now, at least) that echo on means login name, and
77  * echo off means password.
78  */
79
80 static int PAM_conv(int num_msg,
81                     const struct pam_message **msg,
82                     struct pam_response **resp,
83                     void *appdata_ptr)
84 {
85         int replies = 0;
86         struct pam_response *reply = NULL;
87
88         reply = malloc(sizeof(struct pam_response) * num_msg);
89         if (!reply)
90                 return PAM_CONV_ERR;
91
92         for (replies = 0; replies < num_msg; replies++)
93         {
94                 switch (msg[replies]->msg_style)
95                 {
96                         case PAM_PROMPT_ECHO_ON:
97                                 reply[replies].resp_retcode = PAM_SUCCESS;
98                         reply[replies].resp =
99                                         COPY_STRING(PAM_username);
100                                 /* PAM frees resp */
101                                 break;
102
103                         case PAM_PROMPT_ECHO_OFF:
104                                 reply[replies].resp_retcode = PAM_SUCCESS;
105                                 reply[replies].resp =
106                                         COPY_STRING(PAM_password);
107                                 /* PAM frees resp */
108                                 break;
109
110                         case PAM_TEXT_INFO:
111                                 /* fall through */
112
113                         case PAM_ERROR_MSG:
114                                 /* ignore it... */
115                                 reply[replies].resp_retcode = PAM_SUCCESS;
116                                 reply[replies].resp = NULL;
117                                 break;
118
119                         default:
120                                 /* Must be an error of some sort... */
121                                 free(reply);
122                                 return PAM_CONV_ERR;
123                 }
124         }
125         if (reply)
126                 *resp = reply;
127         return PAM_SUCCESS;
128 }
129
130 static struct pam_conv PAM_conversation = {
131         &PAM_conv,
132         NULL
133 };
134
135 static BOOL proc_pam_end(pam_handle_t *pamh)
136 {
137        int pam_error;
138        
139        if( pamh != NULL )
140        {
141                 pam_error = pam_end(pamh, 0);
142                 if(pam_error_handler(pamh, pam_error, "End Cleanup Failed", 2) == True) {
143                         return True;
144                 }
145        }
146        DEBUG(2,("PAM not initialised"));
147        return False;
148 }
149
150
151 static BOOL pam_auth(char *user, char *password)
152 {
153         pam_handle_t *pamh;
154         int pam_error;
155
156         /*
157          * Now use PAM to do authentication.  Bail out if there are any
158          * errors.
159          */
160
161         PAM_password = password;
162         PAM_username = user;
163         DEBUG(4,("PAM Start for User: %s\n", user));
164         pam_error = pam_start("samba", user, &PAM_conversation, &pamh);
165         if(!pam_error_handler(pamh, pam_error, "start failure", 2)) {
166                 proc_pam_end(pamh);
167                 return False;
168         }
169
170         /*
171          * To enable debugging set in /etc/pam.d/samba:
172          *      auth required /lib/security/pam_pwdb.so nullok shadow audit
173          */
174         
175         pam_error = pam_authenticate(pamh, PAM_SILENT); /* Can we authenticate user? */
176         switch( pam_error ){
177                 case PAM_AUTH_ERR:
178                         DEBUG(2, ("PAM: Athentication Error\n"));
179                         break;
180                 case PAM_CRED_INSUFFICIENT:
181                         DEBUG(2, ("PAM: Insufficient Credentials\n"));
182                         break;
183                 case PAM_AUTHINFO_UNAVAIL:
184                         DEBUG(2, ("PAM: Authentication Information Unavailable\n"));
185                         break;
186                 case PAM_USER_UNKNOWN:
187                         DEBUG(2, ("PAM: Username NOT known to Authentication system\n"));
188                         break;
189                 case PAM_MAXTRIES:
190                         DEBUG(2, ("PAM: One or more authentication modules reports user limit exceeeded\n"));
191                         break;
192                 case PAM_ABORT:
193                         DEBUG(0, ("PAM: One or more PAM modules failed to load\n"));
194                         break;
195                 default:
196                         DEBUG(4, ("PAM: User %s Authenticated OK\n", user));
197         }
198         if(!pam_error_handler(pamh, pam_error, "Authentication Failure", 2)) {
199                 proc_pam_end(pamh);
200                 return False;
201         }
202
203         /* 
204          * Now do account management control and validation
205          */
206         pam_error = pam_acct_mgmt(pamh, PAM_SILENT); /* Is user account enabled? */
207         switch( pam_error ) {
208                 case PAM_AUTHTOK_EXPIRED:
209                         DEBUG(2, ("PAM: User is valid but password is expired\n"));
210                         break;
211                 case PAM_ACCT_EXPIRED:
212                         DEBUG(2, ("PAM: User no longer permitted to access system\n"));
213                         break;
214                 case PAM_AUTH_ERR:
215                         DEBUG(2, ("PAM: There was an authentication error\n"));
216                         break;
217                 case PAM_PERM_DENIED:
218                         DEBUG(0, ("PAM: User is NOT permitted to access system at this time\n"));
219                         break;
220                 case PAM_USER_UNKNOWN:
221                         DEBUG(2, ("PAM: User \"%s\" is NOT known to account management\n", user));
222                         break;
223                 default:
224                         DEBUG(4, ("PAM: Account OK for User: %s\n", user));
225         }
226         if(!pam_error_handler(pamh, pam_error, "Account Check Failed", 2)) {
227                 proc_pam_end(pamh);
228                 return False;
229         }
230
231         /*
232          * This will allow samba to aquire a kerberos token. And, when
233          * exporting an AFS cell, be able to /write/ to this cell.
234          */
235
236         pam_error = pam_setcred(pamh, (PAM_ESTABLISH_CRED|PAM_SILENT)); 
237         if(!pam_error_handler(pamh, pam_error, "Set Credential Failure", 2)) {
238                 proc_pam_end(pamh);
239                 return False;
240         }
241         
242         if( !proc_pam_end(pamh))
243                 return False;
244
245         /* If this point is reached, the user has been authenticated. */
246         DEBUG(4, ("PAM: pam_authentication passed for User: %s\n", user));
247         return (True);
248 }
249
250 #if NOTBLOCKEDOUT
251 /* Start PAM authentication for specified account */
252 static BOOL proc_pam_start(pam_handle_t **pamh, char *user)
253 {
254        int pam_error;
255        char * rhost;
256
257        DEBUG(4,("PAM Init for user: %s\n", user));
258
259        pam_error = pam_start("samba", user, &PAM_conversation, pamh);
260        if( !pam_error_handler(*pamh, pam_error, "Init Failed", 0)) {
261                proc_pam_end(*pamh);
262                return False;
263        }
264
265        rhost = client_name();
266        if (strcmp(rhost,"UNKNOWN") == 0)
267                rhost = client_addr();
268
269 #ifdef PAM_RHOST
270        DEBUG(4,("PAM setting rhost to: %s\n", rhost));
271        pam_error = pam_set_item(*pamh, PAM_RHOST, rhost);
272        if(!pam_error_handler(*pamh, pam_error, "set rhost failed", 0)) {
273                proc_pam_end(*pamh);
274                return False;
275        }
276 #endif
277
278 #if defined(PAM_TTY_KLUDGE) && defined(PAM_TTY)
279        pam_error = pam_set_item(*pamh, PAM_TTY, "samba");
280        if (!pam_error_handler(*pamh, pam_error, "set tty failed", 0)) {
281                proc_pam_end(*pamh);
282                return False;
283        }
284 #endif
285
286        return True;
287 }
288
289 static BOOL pam_session(pam_handle_t *pamh, char *user, char *tty, BOOL instance)
290 {
291        int pam_error;
292
293        PAM_password = NULL;
294        PAM_username = user;
295
296 #ifdef PAM_TTY
297        DEBUG(4,("PAM tty set to: %s\"\n", tty));
298        pam_error = pam_set_item(pamh, PAM_TTY, tty);
299        if (!pam_error_handler(pamh, pam_error, "set tty failed", 0)) {
300                proc_pam_end(pamh);
301                return False;
302        }
303 #endif
304
305        if (instance) {
306          pam_error = pam_open_session(pamh, PAM_SILENT);
307          if (!pam_error_handler(pamh, pam_error, "session setup failed", 0)) {
308                proc_pam_end(pamh);
309                return False;
310          }
311        }
312        else
313        {
314          pam_error = pam_close_session(pamh, PAM_SILENT);
315          if (!pam_error_handler(pamh, pam_error, "session close failed", 0)) {
316                proc_pam_end(pamh);
317                return False;
318          }
319        }
320       return (True);
321 }
322
323 static BOOL pam_account(pam_handle_t *pamh, char *user)
324 {
325        int pam_error;
326
327        PAM_password = NULL;
328        PAM_username = user;
329
330        DEBUG(4,("PAM starting account management for user: %s \n", user));
331
332        pam_error = pam_acct_mgmt(pamh, PAM_SILENT);
333        if (!pam_error_handler(pamh, pam_error, "PAM set account management failed", 0)) {
334            proc_pam_end(pamh);
335            return False;
336        } else {
337            DEBUG(4,("PAM account management passed\n"));
338        }
339
340        /*
341         * This will allow samba to aquire a kerberos token. And, when
342         * exporting an AFS cell, be able to /write/ to this cell.
343         */
344        pam_error = pam_setcred(pamh, (PAM_ESTABLISH_CRED));
345        if (!pam_error_handler(pamh, pam_error, "set credentials failed\n", 0)) {
346            proc_pam_end(pamh);
347            return False;
348        }
349
350        /* If this point is reached, the user has been authenticated. */
351        return (True);
352 }
353 static BOOL account_pam(char *user)
354 {
355          /*
356           * Check the account with the PAM account module:
357           *  - This means that accounts can be disabled
358           *    and or expired with avoidance of samba then just
359           *    bypassing the situation.
360           */
361
362          pam_handle_t *pamh = NULL;
363          char * PAMuser;
364
365          PAMuser = malloc(strlen(user)+1);
366          /* This is freed by PAM */
367          strncpy(PAMuser, user, strlen(user)+1);
368
369          if (proc_pam_start(&pamh, PAMuser))
370          {
371            if (pam_account(pamh, PAMuser))
372            {
373              return proc_pam_end(pamh);
374            }
375          }
376          proc_pam_end(pamh);
377          return False;
378 }
379
380 BOOL PAM_session(BOOL instance, const connection_struct *conn, char *tty)
381 {
382         pam_handle_t *pamh=NULL;
383         char * user;
384
385         user = malloc(strlen(conn->user)+1);
386
387         /* This is freed by PAM */
388         strncpy(user, conn->user, strlen(conn->user)+1);
389
390         if (!proc_pam_start(&pamh, user))
391         {
392           proc_pam_end(pamh);
393           return False;
394         }
395
396         if (pam_session(pamh, user, tty, instance))
397         {
398           return proc_pam_end(pamh);
399         }
400         else
401         {
402           proc_pam_end(pamh);
403           return False;
404         }
405 }
406
407 BOOL pam_passcheck(char * user, char * password)
408 {
409         pam_handle_t *pamh = NULL;
410
411         PAM_username = user;
412         PAM_password = password;
413
414         if( proc_pam_start(&pamh, user))
415         {
416                 if( pam_auth(user, password))
417                 {
418                         if( account_pam(user))
419                         {
420                                 return( proc_pam_end(pamh));
421                         }
422                 }       
423         }
424         proc_pam_end(pamh);
425         return( False );
426 }
427 #endif /* NOTBLOCKEDOUT */
428
429 BOOL pam_passcheck( char * user, char * password )
430 {
431         return( pam_auth( user, password ));
432         
433 }
434 #else
435
436  /* Do *NOT* make this function static. Doing so breaks the compile on gcc */
437
438  void pampass_dummy_function( void ) { } /*This stops compiler complaints */
439
440 #endif /* WITH_PAM */