Fix more free twice bugs.
[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 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 extern int DEBUGLEVEL;
34
35 #ifdef WITH_PAM
36
37 /*******************************************************************
38  * Handle PAM authentication 
39  *      - Access, Authentication, Session, Password
40  *   Note: See PAM Documentation and refer to local system PAM implementation
41  *   which determines what actions/limitations/allowances become affected.
42  *********************************************************************/
43
44 #include <security/pam_appl.h>
45
46 /*
47  * Static variables used to communicate between the conversation function
48  * and the server_login function
49  */
50
51 static char *PAM_username;
52 static char *PAM_password;
53
54 /*
55  *  Macros to help make life easy
56  */
57 #define COPY_STRING(s) (s) ? strdup(s) : NULL
58
59 /*
60  * PAM error handler.
61  */
62 static BOOL smb_pam_error_handler(pam_handle_t *pamh, int pam_error, char *msg, int dbglvl)
63 {
64
65         if( pam_error != PAM_SUCCESS) {
66                 DEBUG(dbglvl, ("PAM: %s : %s\n", msg, pam_strerror(pamh, pam_error)));
67                 return False;
68         }
69         return True;
70 }
71
72 /*
73  * PAM conversation function
74  * Here we assume (for now, at least) that echo on means login name, and
75  * echo off means password.
76  */
77
78 static int smb_pam_conv(int num_msg,
79                     const struct pam_message **msg,
80                     struct pam_response **resp,
81                     void *appdata_ptr)
82 {
83         int replies = 0;
84         struct pam_response *reply = NULL;
85
86         reply = malloc(sizeof(struct pam_response) * num_msg);
87         if (!reply)
88                 return PAM_CONV_ERR;
89
90         for (replies = 0; replies < num_msg; replies++) {
91                 switch (msg[replies]->msg_style) {
92                         case PAM_PROMPT_ECHO_ON:
93                                 reply[replies].resp_retcode = PAM_SUCCESS;
94                         reply[replies].resp =
95                                         COPY_STRING(PAM_username);
96                                 /* PAM frees resp */
97                                 break;
98
99                         case PAM_PROMPT_ECHO_OFF:
100                                 reply[replies].resp_retcode = PAM_SUCCESS;
101                                 reply[replies].resp =
102                                         COPY_STRING(PAM_password);
103                                 /* PAM frees resp */
104                                 break;
105
106                         case PAM_TEXT_INFO:
107                                 /* fall through */
108
109                         case PAM_ERROR_MSG:
110                                 /* ignore it... */
111                                 reply[replies].resp_retcode = PAM_SUCCESS;
112                                 reply[replies].resp = NULL;
113                                 break;
114
115                         default:
116                                 /* Must be an error of some sort... */
117                                 free(reply);
118                                 return PAM_CONV_ERR;
119                 }
120         }
121         if (reply)
122                 *resp = reply;
123         return PAM_SUCCESS;
124 }
125
126 static struct pam_conv smb_pam_conversation = {
127         &smb_pam_conv,
128         NULL
129 };
130
131 /* 
132  * PAM Closing out cleanup handler
133  */
134 static BOOL smb_pam_end(pam_handle_t *pamh)
135 {
136         int pam_error;
137        
138         if( pamh != NULL ) {
139                 pam_error = pam_end(pamh, 0);
140                 if(smb_pam_error_handler(pamh, pam_error, "End Cleanup Failed", 2) == True) {
141                         DEBUG(4, ("PAM: PAM_END OK.\n"));
142                         return True;
143                 }
144         }
145         DEBUG(2,("PAM: not initialised"));
146         return False;
147 }
148
149 /*
150  * Start PAM authentication for specified account
151  */
152 static BOOL smb_pam_start(pam_handle_t **pamh, char *user, char *rhost)
153 {
154         int pam_error;
155
156         *pamh = (pam_handle_t *)NULL;
157
158         DEBUG(4,("PAM: Init user: %s\n", user));
159
160         pam_error = pam_start("samba", user, &smb_pam_conversation, pamh);
161         if( !smb_pam_error_handler(*pamh, pam_error, "Init Failed", 0)) {
162                 *pamh = (pam_handle_t *)NULL;
163                 return False;
164         }
165
166         if (rhost == NULL) {
167                 rhost = client_name();
168                 if (strequal(rhost,"UNKNOWN"))
169                         rhost = client_addr();
170         }
171
172 #ifdef PAM_RHOST
173         DEBUG(4,("PAM: setting rhost to: %s\n", rhost));
174         pam_error = pam_set_item(*pamh, PAM_RHOST, rhost);
175         if(!smb_pam_error_handler(*pamh, pam_error, "set rhost failed", 0)) {
176                 smb_pam_end(*pamh);
177                 *pamh = (pam_handle_t *)NULL;
178                 return False;
179         }
180 #endif
181 #ifdef PAM_TTY
182         DEBUG(4,("PAM: setting tty\n"));
183         pam_error = pam_set_item(*pamh, PAM_TTY, "samba");
184         if (!smb_pam_error_handler(*pamh, pam_error, "set tty failed", 0)) {
185                 smb_pam_end(*pamh);
186                 *pamh = (pam_handle_t *)NULL;
187                 return False;
188         }
189 #endif
190         DEBUG(4,("PAM: Init passed for user: %s\n", user));
191         return True;
192 }
193
194 /*
195  * PAM Authentication Handler
196  */
197 static BOOL smb_pam_auth(pam_handle_t *pamh, char *user, char *password)
198 {
199         int pam_error;
200
201         /*
202          * To enable debugging set in /etc/pam.d/samba:
203          *      auth required /lib/security/pam_pwdb.so nullok shadow audit
204          */
205         
206         DEBUG(4,("PAM: Authenticate User: %s\n", user));
207         pam_error = pam_authenticate(pamh, PAM_SILENT); /* Can we authenticate user? */
208         switch( pam_error ){
209                 case PAM_AUTH_ERR:
210                         DEBUG(2, ("PAM: Athentication Error\n"));
211                         break;
212                 case PAM_CRED_INSUFFICIENT:
213                         DEBUG(2, ("PAM: Insufficient Credentials\n"));
214                         break;
215                 case PAM_AUTHINFO_UNAVAIL:
216                         DEBUG(2, ("PAM: Authentication Information Unavailable\n"));
217                         break;
218                 case PAM_USER_UNKNOWN:
219                         DEBUG(2, ("PAM: Username NOT known to Authentication system\n"));
220                         break;
221                 case PAM_MAXTRIES:
222                         DEBUG(2, ("PAM: One or more authentication modules reports user limit exceeeded\n"));
223                         break;
224                 case PAM_ABORT:
225                         DEBUG(0, ("PAM: One or more PAM modules failed to load\n"));
226                         break;
227                 case PAM_SUCCESS:
228                         DEBUG(4, ("PAM: User %s Authenticated OK\n", user));
229                         break;
230                 default:
231                         DEBUG(0, ("PAM: UNKNOWN ERROR while authenticating user %s\n", user));
232         }
233         if(!smb_pam_error_handler(pamh, pam_error, "Authentication Failure", 2)) {
234                 smb_pam_end(pamh);
235                 return False;
236         }
237         /* If this point is reached, the user has been authenticated. */
238         return (True);
239 }
240
241 /* 
242  * PAM Account Handler
243  */
244 static BOOL smb_pam_account(pam_handle_t *pamh, char * user, char * password, BOOL pam_auth)
245 {
246         int pam_error;
247
248         DEBUG(4,("PAM: Account Management for User: %s\n", user));
249         pam_error = pam_acct_mgmt(pamh, PAM_SILENT); /* Is user account enabled? */
250         switch( pam_error ) {
251                 case PAM_AUTHTOK_EXPIRED:
252                         DEBUG(2, ("PAM: User is valid but password is expired\n"));
253                         break;
254                 case PAM_ACCT_EXPIRED:
255                         DEBUG(2, ("PAM: User no longer permitted to access system\n"));
256                         break;
257                 case PAM_AUTH_ERR:
258                         DEBUG(2, ("PAM: There was an authentication error\n"));
259                         break;
260                 case PAM_PERM_DENIED:
261                         DEBUG(0, ("PAM: User is NOT permitted to access system at this time\n"));
262                         break;
263                 case PAM_USER_UNKNOWN:
264                         DEBUG(0, ("PAM: User \"%s\" is NOT known to account management\n", user));
265                         break;
266                 case PAM_SUCCESS:
267                         DEBUG(4, ("PAM: Account OK for User: %s\n", user));
268                         break;
269                 default:
270                         DEBUG(0, ("PAM: UNKNOWN ERROR for User: %s\n", user));
271         }
272         if(!smb_pam_error_handler(pamh, pam_error, "Account Check Failed", 2)) {
273                 smb_pam_end(pamh);
274                 return False;
275         }
276
277         /* Skip the pam_setcred() call if we didn't use pam_authenticate()
278            for authentication -- it's an error to call pam_setcred without
279            calling pam_authenticate first */
280         if (!pam_auth) {
281                 DEBUG(4, ("PAM: Skipping setcred for user: %s (using encrypted passwords)\n", user));
282                 return True;
283         }
284
285         /*
286          * This will allow samba to aquire a kerberos token. And, when
287          * exporting an AFS cell, be able to /write/ to this cell.
288          */
289
290         DEBUG(4,("PAM: Account Management SetCredentials for User: %s\n", user));
291         pam_error = pam_setcred(pamh, (PAM_ESTABLISH_CRED|PAM_SILENT)); 
292         switch( pam_error ) {
293                 case PAM_CRED_UNAVAIL:
294                         DEBUG(0, ("PAM: Credentials not found for user:%s", user ));
295                         break;
296                 case PAM_CRED_EXPIRED:
297                         DEBUG(0, ("PAM: Credentials for user: \"%s\" EXPIRED!", user ));
298                         break;
299                 case PAM_USER_UNKNOWN:
300                         DEBUG(0, ("PAM: User: \"%s\" is NOT known so can not set credentials!", user ));
301                         break;
302                 case PAM_CRED_ERR:
303                         DEBUG(0, ("PAM: Unknown setcredentials error - unable to set credentials for %s", user ));
304                         break;
305                 case PAM_SUCCESS:
306                         DEBUG(4, ("PAM: SetCredentials OK for User: %s\n", user));
307                         break;
308                 default:
309                         DEBUG(0, ("PAM: Error Condition Unknown in pam_setcred function call!"));
310         }
311         if(!smb_pam_error_handler(pamh, pam_error, "Set Credential Failure", 2)) {
312                 smb_pam_end(pamh);
313                 return False;
314         }
315         
316         /* If this point is reached, the user has been authenticated. */
317         return (True);
318 }
319
320
321 /*
322  * PAM Internal Session Handler
323  */
324 static BOOL smb_internal_pam_session(pam_handle_t *pamh, char *user, char *tty, BOOL flag)
325 {
326         int pam_error;
327
328         PAM_password = NULL;
329         PAM_username = user;
330
331 #ifdef PAM_TTY
332         DEBUG(4,("PAM: tty set to: %s\n", tty));
333         pam_error = pam_set_item(pamh, PAM_TTY, tty);
334         if (!smb_pam_error_handler(pamh, pam_error, "set tty failed", 0))
335                 return False;
336 #endif
337
338         if (flag) {
339                 pam_error = pam_open_session(pamh, PAM_SILENT);
340                 if (!smb_pam_error_handler(pamh, pam_error, "session setup failed", 0))
341                         return False;
342         } else {
343                 pam_error = pam_close_session(pamh, PAM_SILENT);
344                 if (!smb_pam_error_handler(pamh, pam_error, "session close failed", 0))
345                         return False;
346         }
347         return (True);
348 }
349
350 /*
351  * PAM Externally accessible Session handler
352  */
353 BOOL smb_pam_session(BOOL flag, const char *in_user, char *tty, char *rhost)
354 {
355         pam_handle_t *pamh = NULL;
356         char * user;
357
358         user = strdup(in_user);
359         if ( user == NULL ) {
360                 DEBUG(0, ("PAM: PAM_session Malloc Failed!\n"));
361                 return False;
362         }
363
364         if (!smb_pam_start(&pamh, user, rhost)) {
365                 return False;
366         }
367
368         if (!smb_internal_pam_session(pamh, user, tty, flag)) {
369                 smb_pam_end(pamh);
370                 return False;
371         }
372         return smb_pam_end(pamh);
373 }
374
375 /*
376  * PAM Externally accessible Account handler
377  */
378 BOOL smb_pam_accountcheck(char * user)
379 {
380         pam_handle_t *pamh = NULL;
381
382         PAM_username = user;
383         PAM_password = NULL;
384
385         if( smb_pam_start(&pamh, user, NULL)) {
386                 if ( smb_pam_account(pamh, user, NULL, False)) {
387                         return( smb_pam_end(pamh));
388                 }
389         }
390         DEBUG(0, ("PAM: Account Validation Failed - Rejecting User!\n"));
391         return( False );
392 }
393
394 /*
395  * PAM Password Validation Suite
396  */
397 BOOL smb_pam_passcheck(char * user, char * password)
398 {
399         pam_handle_t *pamh = NULL;
400
401         PAM_username = user;
402         PAM_password = password;
403
404         if( smb_pam_start(&pamh, user, NULL)) {
405                 if ( smb_pam_auth(pamh, user, password)) {
406                         if ( smb_pam_account(pamh, user, password, True)) {
407                                 return( smb_pam_end(pamh));
408                         }
409                 }
410         }
411         DEBUG(0, ("PAM: System Validation Failed - Rejecting User!\n"));
412         return( False );
413 }
414
415 #else
416
417 /* If PAM not used, no PAM restrictions on accounts. */
418  BOOL smb_pam_accountcheck(char * user)
419 {
420         return True;
421 }
422
423 #endif /* WITH_PAM */