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