This commit was manufactured by cvs2svn to create branch 'SAMBA_3_0'.(This used to...
[ira/wip.git] / source3 / pam_smbpass / pam_smb_passwd.c
1 /*
2    Unix SMB/CIFS implementation.
3    Use PAM to update user passwords in the local SAM
4    Copyright (C) Steve Langasek         1998-2003
5    
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19
20 */
21
22 /* indicate the following groups are defined */
23 #define PAM_SM_PASSWORD
24
25 #include "includes.h"
26
27 /* This is only used in the Sun implementation.  FIXME: we really
28    want a define here that distinguishes between the Solaris PAM
29    and others (including FreeBSD). */
30
31 #ifndef LINUX
32 #include <security/pam_appl.h>
33 #endif
34
35 #include <security/pam_modules.h>
36
37 #include "general.h" 
38
39 #include "support.h"
40
41 int smb_update_db( pam_handle_t *pamh, int ctrl, const char *user,  const char *pass_new )
42 {
43         int retval;
44         pstring err_str;
45         pstring msg_str;
46
47         err_str[0] = '\0';
48         msg_str[0] = '\0';
49
50         retval = local_password_change( user, LOCAL_SET_PASSWORD, pass_new,
51                                         err_str, sizeof(err_str),
52                                         msg_str, sizeof(msg_str) );
53
54         if (!retval) {
55                 if (*err_str) {
56                         err_str[PSTRING_LEN-1] = '\0';
57                         make_remark( pamh, ctrl, PAM_ERROR_MSG, err_str );
58                 }
59
60                 /* FIXME: what value is appropriate here? */
61                 retval = PAM_AUTHTOK_ERR;
62         } else {
63                 if (*msg_str) {
64                         msg_str[PSTRING_LEN-1] = '\0';
65                         make_remark( pamh, ctrl, PAM_TEXT_INFO, msg_str );
66                 }
67                 retval = PAM_SUCCESS;
68         }
69
70         return retval;      
71 }
72
73
74 /* data tokens */
75
76 #define _SMB_OLD_AUTHTOK  "-SMB-OLD-PASS"
77 #define _SMB_NEW_AUTHTOK  "-SMB-NEW-PASS"
78
79 /*
80  * FUNCTION: pam_sm_chauthtok()
81  *
82  * This function is called twice by the PAM library, once with
83  * PAM_PRELIM_CHECK set, and then again with PAM_UPDATE_AUTHTOK set.  With
84  * Linux-PAM, these two passes generally involve first checking the old
85  * token and then changing the token.  This is what we do here.
86  *
87  * Having obtained a new password. The function updates the
88  * SMB_PASSWD_FILE file (normally, $(LIBDIR)/smbpasswd).
89  */
90
91 int pam_sm_chauthtok(pam_handle_t *pamh, int flags,
92                      int argc, const char **argv)
93 {
94     unsigned int ctrl;
95     int retval;
96
97     extern BOOL in_client;
98
99     SAM_ACCOUNT *sampass = NULL;
100     void (*oldsig_handler)(int);
101     const char *user;
102     char *pass_old;
103     char *pass_new;
104
105     NTSTATUS nt_status;
106
107     /* Samba initialization. */
108     setup_logging( "pam_smbpass", False );
109     in_client = True;
110
111     ctrl = set_ctrl(flags, argc, argv);
112
113     /*
114      * First get the name of a user.  No need to do anything if we can't
115      * determine this.
116      */
117
118     retval = pam_get_user( pamh, &user, "Username: " );
119     if (retval != PAM_SUCCESS) {
120         if (on( SMB_DEBUG, ctrl )) {
121             _log_err( LOG_DEBUG, "password: could not identify user" );
122         }
123         return retval;
124     }
125     if (on( SMB_DEBUG, ctrl )) {
126         _log_err( LOG_DEBUG, "username [%s] obtained", user );
127     }
128
129     /* Getting into places that might use LDAP -- protect the app
130        from a SIGPIPE it's not expecting */
131     oldsig_handler = CatchSignal(SIGPIPE, SIGNAL_CAST SIG_IGN);
132
133     if (!initialize_password_db(True)) {
134         _log_err( LOG_ALERT, "Cannot access samba password database" );
135         CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
136         return PAM_AUTHINFO_UNAVAIL;
137     }
138
139     /* obtain user record */
140     if (!NT_STATUS_IS_OK(nt_status = pdb_init_sam(&sampass))) {
141         CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
142         return nt_status_to_pam(nt_status);
143     }
144
145     if (!pdb_getsampwnam(sampass,user)) {
146         _log_err( LOG_ALERT, "Failed to find entry for user %s.", user );
147         CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
148         return PAM_USER_UNKNOWN;
149     }
150
151     if (flags & PAM_PRELIM_CHECK) {
152         /*
153          * obtain and verify the current password (OLDAUTHTOK) for
154          * the user.
155          */
156
157         char *Announce;
158
159         if (_smb_blankpasswd( ctrl, sampass )) {
160
161             pdb_free_sam(&sampass);
162             CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
163             return PAM_SUCCESS;
164         }
165
166         /* Password change by root, or for an expired token, doesn't
167            require authentication.  Is this a good choice? */
168         if (getuid() != 0 && !(flags & PAM_CHANGE_EXPIRED_AUTHTOK)) {
169
170             /* tell user what is happening */
171 #define greeting "Changing password for "
172             Announce = (char *) malloc(sizeof(greeting)+strlen(user));
173             if (Announce == NULL) {
174                 _log_err(LOG_CRIT, "password: out of memory");
175                 pdb_free_sam(&sampass);
176                 CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
177                 return PAM_BUF_ERR;
178             }
179             strncpy( Announce, greeting, sizeof(greeting) );
180             strncpy( Announce+sizeof(greeting)-1, user, strlen(user)+1 );
181 #undef greeting
182
183             set( SMB__OLD_PASSWD, ctrl );
184             retval = _smb_read_password( pamh, ctrl, Announce, "Current SMB password: ",
185                                          NULL, _SMB_OLD_AUTHTOK, &pass_old );
186             SAFE_FREE( Announce );
187
188             if (retval != PAM_SUCCESS) {
189                 _log_err( LOG_NOTICE
190                           , "password - (old) token not obtained" );
191                 pdb_free_sam(&sampass);
192                 CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
193                 return retval;
194             }
195
196             /* verify that this is the password for this user */
197
198             retval = _smb_verify_password( pamh, sampass, pass_old, ctrl );
199
200         } else {
201             pass_old = NULL;
202             retval = PAM_SUCCESS;           /* root doesn't have to */
203         }
204
205         pass_old = NULL;
206         pdb_free_sam(&sampass);
207         CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
208         return retval;
209
210     } else if (flags & PAM_UPDATE_AUTHTOK) {
211
212         /*
213          * obtain the proposed password
214          */
215
216         /*
217          * get the old token back. NULL was ok only if root [at this
218          * point we assume that this has already been enforced on a
219          * previous call to this function].
220          */
221
222         if (off( SMB_NOT_SET_PASS, ctrl )) {
223             retval = pam_get_item( pamh, PAM_OLDAUTHTOK,
224                                    (const void **)&pass_old );
225         } else {
226             retval = pam_get_data( pamh, _SMB_OLD_AUTHTOK,
227                                    (const void **)&pass_old );
228             if (retval == PAM_NO_MODULE_DATA) {
229                 pass_old = NULL;
230                 retval = PAM_SUCCESS;
231             }
232         }
233
234         if (retval != PAM_SUCCESS) {
235             _log_err( LOG_NOTICE, "password: user not authenticated" );
236             pdb_free_sam(&sampass);
237             CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
238             return retval;
239         }
240
241         /*
242          * use_authtok is to force the use of a previously entered
243          * password -- needed for pluggable password strength checking
244          * or other module stacking
245          */
246
247         if (on( SMB_USE_AUTHTOK, ctrl )) {
248             set( SMB_USE_FIRST_PASS, ctrl );
249         }
250
251         retval = _smb_read_password( pamh, ctrl
252                                       , NULL
253                                       , "Enter new SMB password: "
254                                       , "Retype new SMB password: "
255                                       , _SMB_NEW_AUTHTOK
256                                       , &pass_new );
257
258         if (retval != PAM_SUCCESS) {
259             if (on( SMB_DEBUG, ctrl )) {
260                 _log_err( LOG_ALERT
261                           , "password: new password not obtained" );
262             }
263             pass_old = NULL;                               /* tidy up */
264             pdb_free_sam(&sampass);
265             CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
266             return retval;
267         }
268
269         /*
270          * At this point we know who the user is and what they
271          * propose as their new password. Verify that the new
272          * password is acceptable.
273          */ 
274
275         if (pass_new[0] == '\0') {     /* "\0" password = NULL */
276             pass_new = NULL;
277         }
278
279         retval = _pam_smb_approve_pass(pamh, ctrl, pass_old, pass_new);
280
281         if (retval != PAM_SUCCESS) {
282             _log_err(LOG_NOTICE, "new password not acceptable");
283             pass_new = pass_old = NULL;               /* tidy up */
284             pdb_free_sam(&sampass);
285             CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
286             return retval;
287         }
288
289         /*
290          * By reaching here we have approved the passwords and must now
291          * rebuild the smb password file.
292          */
293
294         /* update the password database */
295
296         retval = smb_update_db(pamh, ctrl, user, pass_new);
297         if (retval == PAM_SUCCESS) {
298             /* password updated */
299             _log_err( LOG_NOTICE, "password for (%s/%d) changed by (%s/%d)"
300                       , user, pdb_get_uid(sampass), uidtoname( getuid() )
301                       , getuid() );
302         } else {
303             _log_err( LOG_ERR, "password change failed for user %s"
304                       , user );
305         }
306
307         pass_old = pass_new = NULL;
308         if (sampass) {
309                 pdb_free_sam(&sampass);
310                 sampass = NULL;
311         }
312
313     } else {            /* something has broken with the library */
314
315         _log_err( LOG_ALERT, "password received unknown request" );
316         retval = PAM_ABORT;
317
318     }
319     
320     if (sampass) {
321         pdb_free_sam(&sampass);
322         sampass = NULL;
323     }
324
325     pdb_free_sam(&sampass);
326     CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
327     return retval;
328 }
329
330 /* static module data */
331 #ifdef PAM_STATIC
332 struct pam_module _pam_smbpass_passwd_modstruct = {
333      "pam_smbpass",
334      NULL,
335      NULL,
336      NULL,
337      NULL,
338      NULL,
339      pam_sm_chauthtok
340 };
341 #endif
342