s3:pam_smbpass: don't call openlog() or closelog() from pam_smbpass
[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 3 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, see <http://www.gnu.org/licenses/>.
18
19 */
20
21 /* indicate the following groups are defined */
22 #define PAM_SM_PASSWORD
23
24 #include "includes.h"
25
26 /* This is only used in the Sun implementation.  FIXME: we really
27    want a define here that distinguishes between the Solaris PAM
28    and others (including FreeBSD). */
29
30 #ifndef LINUX
31 #if defined(HAVE_SECURITY_PAM_APPL_H)
32 #include <security/pam_appl.h>
33 #elif defined(HAVE_PAM_PAM_APPL_H)
34 #include <pam/pam_appl.h>
35 #endif
36 #endif
37
38 #if defined(HAVE_SECURITY_PAM_MODULES_H)
39 #include <security/pam_modules.h>
40 #elif defined(HAVE_PAM_PAM_MODULES_H)
41 #include <pam/pam_modules.h>
42 #endif
43
44 #include "general.h" 
45
46 #include "support.h"
47
48 int smb_update_db( pam_handle_t *pamh, int ctrl, const char *user,  const char *pass_new )
49 {
50         int retval;
51         char *err_str = NULL;
52         char *msg_str = NULL;
53
54         retval = NT_STATUS_IS_OK(local_password_change(user, LOCAL_SET_PASSWORD, pass_new,
55                                         &err_str,
56                                         &msg_str));
57
58         if (!retval) {
59                 if (err_str) {
60                         make_remark(pamh, ctrl, PAM_ERROR_MSG, err_str );
61                 }
62
63                 /* FIXME: what value is appropriate here? */
64                 retval = PAM_AUTHTOK_ERR;
65         } else {
66                 if (msg_str) {
67                         make_remark(pamh, ctrl, PAM_TEXT_INFO, msg_str );
68                 }
69                 retval = PAM_SUCCESS;
70         }
71
72         SAFE_FREE(err_str);
73         SAFE_FREE(msg_str);
74         return retval;      
75 }
76
77
78 /* data tokens */
79
80 #define _SMB_OLD_AUTHTOK  "-SMB-OLD-PASS"
81 #define _SMB_NEW_AUTHTOK  "-SMB-NEW-PASS"
82
83 /*
84  * FUNCTION: pam_sm_chauthtok()
85  *
86  * This function is called twice by the PAM library, once with
87  * PAM_PRELIM_CHECK set, and then again with PAM_UPDATE_AUTHTOK set.  With
88  * Linux-PAM, these two passes generally involve first checking the old
89  * token and then changing the token.  This is what we do here.
90  *
91  * Having obtained a new password. The function updates the
92  * SMB_PASSWD_FILE file (normally, $(LIBDIR)/smbpasswd).
93  */
94
95 int pam_sm_chauthtok(pam_handle_t *pamh, int flags,
96                      int argc, const char **argv)
97 {
98     unsigned int ctrl;
99     int retval;
100
101     struct samu *sampass = NULL;
102     void (*oldsig_handler)(int);
103     const char *user;
104     char *pass_old;
105     char *pass_new;
106
107     /* Samba initialization. */
108     load_case_tables();
109     lp_set_in_client(True);
110
111     ctrl = set_ctrl(pamh, 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(pamh, LOG_DEBUG, "password: could not identify user");
122         }
123         return retval;
124     }
125     if (on( SMB_DEBUG, ctrl )) {
126         _log_err(pamh, LOG_DEBUG, "username [%s] obtained", user);
127     }
128
129     if (geteuid() != 0) {
130         _log_err(pamh, LOG_DEBUG, "Cannot access samba password database, not running as root.");
131         return PAM_AUTHINFO_UNAVAIL;
132     }
133
134     /* Getting into places that might use LDAP -- protect the app
135        from a SIGPIPE it's not expecting */
136     oldsig_handler = CatchSignal(SIGPIPE, SIGNAL_CAST SIG_IGN);
137
138     if (!initialize_password_db(False, NULL)) {
139       _log_err(pamh, LOG_ALERT, "Cannot access samba password database" );
140         CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
141         return PAM_AUTHINFO_UNAVAIL;
142     }
143
144     /* obtain user record */
145     if ( !(sampass = samu_new( NULL )) ) {
146         CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
147         return nt_status_to_pam(NT_STATUS_NO_MEMORY);
148     }
149
150     if (!pdb_getsampwnam(sampass,user)) {
151         _log_err(pamh, LOG_ALERT, "Failed to find entry for user %s.", user);
152         CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
153         return PAM_USER_UNKNOWN;
154     }
155     if (on( SMB_DEBUG, ctrl )) {
156         _log_err(pamh, LOG_DEBUG, "Located account for %s", user);
157     }
158
159     if (flags & PAM_PRELIM_CHECK) {
160         /*
161          * obtain and verify the current password (OLDAUTHTOK) for
162          * the user.
163          */
164
165         char *Announce;
166
167         if (_smb_blankpasswd( ctrl, sampass )) {
168
169             TALLOC_FREE(sampass);
170             CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
171             return PAM_SUCCESS;
172         }
173
174         /* Password change by root, or for an expired token, doesn't
175            require authentication.  Is this a good choice? */
176         if (getuid() != 0 && !(flags & PAM_CHANGE_EXPIRED_AUTHTOK)) {
177
178             /* tell user what is happening */
179 #define greeting "Changing password for "
180             Announce = SMB_MALLOC_ARRAY(char, sizeof(greeting)+strlen(user));
181             if (Announce == NULL) {
182                 _log_err(pamh, LOG_CRIT, "password: out of memory");
183                 TALLOC_FREE(sampass);
184                 CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
185                 return PAM_BUF_ERR;
186             }
187             strncpy( Announce, greeting, sizeof(greeting) );
188             strncpy( Announce+sizeof(greeting)-1, user, strlen(user)+1 );
189 #undef greeting
190
191             set( SMB__OLD_PASSWD, ctrl );
192             retval = _smb_read_password( pamh, ctrl, Announce, "Current SMB password: ",
193                                          NULL, _SMB_OLD_AUTHTOK, &pass_old );
194             SAFE_FREE( Announce );
195
196             if (retval != PAM_SUCCESS) {
197                 _log_err(pamh, LOG_NOTICE,
198                          "password - (old) token not obtained");
199                 TALLOC_FREE(sampass);
200                 CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
201                 return retval;
202             }
203
204             /* verify that this is the password for this user */
205
206             retval = _smb_verify_password( pamh, sampass, pass_old, ctrl );
207
208         } else {
209             pass_old = NULL;
210             retval = PAM_SUCCESS;           /* root doesn't have to */
211         }
212
213         pass_old = NULL;
214         TALLOC_FREE(sampass);
215         CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
216         return retval;
217
218     } else if (flags & PAM_UPDATE_AUTHTOK) {
219
220         /*
221          * obtain the proposed password
222          */
223
224         /*
225          * get the old token back. NULL was ok only if root [at this
226          * point we assume that this has already been enforced on a
227          * previous call to this function].
228          */
229
230         if (off( SMB_NOT_SET_PASS, ctrl )) {
231             retval = _pam_get_item( pamh, PAM_OLDAUTHTOK,
232                                    &pass_old );
233         } else {
234             retval = _pam_get_data( pamh, _SMB_OLD_AUTHTOK,
235                                    &pass_old );
236             if (retval == PAM_NO_MODULE_DATA) {
237                 pass_old = NULL;
238                 retval = PAM_SUCCESS;
239             }
240         }
241
242         if (retval != PAM_SUCCESS) {
243             _log_err(pamh, LOG_NOTICE, "password: user not authenticated");
244             TALLOC_FREE(sampass);
245             CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
246             return retval;
247         }
248
249         /*
250          * use_authtok is to force the use of a previously entered
251          * password -- needed for pluggable password strength checking
252          * or other module stacking
253          */
254
255         if (on( SMB_USE_AUTHTOK, ctrl )) {
256             set( SMB_USE_FIRST_PASS, ctrl );
257         }
258
259         retval = _smb_read_password( pamh, ctrl
260                                       , NULL
261                                       , "Enter new SMB password: "
262                                       , "Retype new SMB password: "
263                                       , _SMB_NEW_AUTHTOK
264                                       , &pass_new );
265
266         if (retval != PAM_SUCCESS) {
267             if (on( SMB_DEBUG, ctrl )) {
268                 _log_err(pamh, LOG_ALERT,
269                          "password: new password not obtained");
270             }
271             pass_old = NULL;                               /* tidy up */
272             TALLOC_FREE(sampass);
273             CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
274             return retval;
275         }
276
277         /*
278          * At this point we know who the user is and what they
279          * propose as their new password. Verify that the new
280          * password is acceptable.
281          */ 
282
283         if (pass_new[0] == '\0') {     /* "\0" password = NULL */
284             pass_new = NULL;
285         }
286
287         retval = _pam_smb_approve_pass(pamh, ctrl, pass_old, pass_new);
288
289         if (retval != PAM_SUCCESS) {
290             _log_err(pamh, LOG_NOTICE, "new password not acceptable");
291             pass_new = pass_old = NULL;               /* tidy up */
292             TALLOC_FREE(sampass);
293             CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
294             return retval;
295         }
296
297         /*
298          * By reaching here we have approved the passwords and must now
299          * rebuild the smb password file.
300          */
301
302         /* update the password database */
303
304         retval = smb_update_db(pamh, ctrl, user, pass_new);
305         if (retval == PAM_SUCCESS) {
306             uid_t uid;
307             
308             /* password updated */
309                 if (!sid_to_uid(pdb_get_user_sid(sampass), &uid)) {
310                         _log_err(pamh, LOG_NOTICE,
311                                  "Unable to get uid for user %s",
312                                 pdb_get_username(sampass));
313                         _log_err(pamh, LOG_NOTICE, "password for (%s) changed by (%s/%d)",
314                                 user, uidtoname(getuid()), getuid());
315                 } else {
316                         _log_err(pamh, LOG_NOTICE, "password for (%s/%d) changed by (%s/%d)",
317                                 user, uid, uidtoname(getuid()), getuid());
318                 }
319         } else {
320                 _log_err(pamh, LOG_ERR, "password change failed for user %s", user);
321         }
322
323         pass_old = pass_new = NULL;
324         if (sampass) {
325                 TALLOC_FREE(sampass);
326                 sampass = NULL;
327         }
328
329     } else {            /* something has broken with the library */
330
331         _log_err(pamh, LOG_ALERT, "password received unknown request");
332         retval = PAM_ABORT;
333
334     }
335     
336     if (sampass) {
337         TALLOC_FREE(sampass);
338         sampass = NULL;
339     }
340
341     TALLOC_FREE(sampass);
342     CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
343     return retval;
344 }
345
346 /* static module data */
347 #ifdef PAM_STATIC
348 struct pam_module _pam_smbpass_passwd_modstruct = {
349      "pam_smbpass",
350      NULL,
351      NULL,
352      NULL,
353      NULL,
354      NULL,
355      pam_sm_chauthtok
356 };
357 #endif
358