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