~vcs-imports/samba/main

« back to all changes in this revision

Viewing changes to source/pam_smbpass/pam_smb_passwd.c

  • Committer: jerry
  • Date: 2006-07-14 21:48:39 UTC
  • Revision ID: vcs-imports@canonical.com-20060714214839-586d8c489a8fcead
gutting trunk to move to svn:externals

Show diffs side-by-side

added added

removed removed

Lines of Context:
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 = NT_STATUS_IS_OK(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
 
    struct samu *sampass = NULL;
100
 
    void (*oldsig_handler)(int);
101
 
    const char *user;
102
 
    char *pass_old;
103
 
    char *pass_new;
104
 
 
105
 
    /* Samba initialization. */
106
 
    load_case_tables();
107
 
    setup_logging( "pam_smbpass", False );
108
 
    in_client = True;
109
 
 
110
 
    ctrl = set_ctrl(flags, argc, argv);
111
 
 
112
 
    /*
113
 
     * First get the name of a user.  No need to do anything if we can't
114
 
     * determine this.
115
 
     */
116
 
 
117
 
    retval = pam_get_user( pamh, &user, "Username: " );
118
 
    if (retval != PAM_SUCCESS) {
119
 
        if (on( SMB_DEBUG, ctrl )) {
120
 
            _log_err( LOG_DEBUG, "password: could not identify user" );
121
 
        }
122
 
        return retval;
123
 
    }
124
 
    if (on( SMB_DEBUG, ctrl )) {
125
 
        _log_err( LOG_DEBUG, "username [%s] obtained", user );
126
 
    }
127
 
 
128
 
    /* Getting into places that might use LDAP -- protect the app
129
 
       from a SIGPIPE it's not expecting */
130
 
    oldsig_handler = CatchSignal(SIGPIPE, SIGNAL_CAST SIG_IGN);
131
 
 
132
 
    if (!initialize_password_db(False)) {
133
 
        _log_err( LOG_ALERT, "Cannot access samba password database" );
134
 
        CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
135
 
        return PAM_AUTHINFO_UNAVAIL;
136
 
    }
137
 
 
138
 
    /* obtain user record */
139
 
    if ( !(sampass = samu_new( NULL )) ) {
140
 
        CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
141
 
        return nt_status_to_pam(NT_STATUS_NO_MEMORY);
142
 
    }
143
 
 
144
 
    if (!pdb_getsampwnam(sampass,user)) {
145
 
        _log_err( LOG_ALERT, "Failed to find entry for user %s.", user );
146
 
        CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
147
 
        return PAM_USER_UNKNOWN;
148
 
    }
149
 
    if (on( SMB_DEBUG, ctrl )) {
150
 
        _log_err( LOG_DEBUG, "Located account for %s", user );
151
 
    }
152
 
 
153
 
    if (flags & PAM_PRELIM_CHECK) {
154
 
        /*
155
 
         * obtain and verify the current password (OLDAUTHTOK) for
156
 
         * the user.
157
 
         */
158
 
 
159
 
        char *Announce;
160
 
 
161
 
        if (_smb_blankpasswd( ctrl, sampass )) {
162
 
 
163
 
            TALLOC_FREE(sampass);
164
 
            CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
165
 
            return PAM_SUCCESS;
166
 
        }
167
 
 
168
 
        /* Password change by root, or for an expired token, doesn't
169
 
           require authentication.  Is this a good choice? */
170
 
        if (getuid() != 0 && !(flags & PAM_CHANGE_EXPIRED_AUTHTOK)) {
171
 
 
172
 
            /* tell user what is happening */
173
 
#define greeting "Changing password for "
174
 
            Announce = SMB_MALLOC_ARRAY(char, sizeof(greeting)+strlen(user));
175
 
            if (Announce == NULL) {
176
 
                _log_err(LOG_CRIT, "password: out of memory");
177
 
                TALLOC_FREE(sampass);
178
 
                CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
179
 
                return PAM_BUF_ERR;
180
 
            }
181
 
            strncpy( Announce, greeting, sizeof(greeting) );
182
 
            strncpy( Announce+sizeof(greeting)-1, user, strlen(user)+1 );
183
 
#undef greeting
184
 
 
185
 
            set( SMB__OLD_PASSWD, ctrl );
186
 
            retval = _smb_read_password( pamh, ctrl, Announce, "Current SMB password: ",
187
 
                                         NULL, _SMB_OLD_AUTHTOK, &pass_old );
188
 
            SAFE_FREE( Announce );
189
 
 
190
 
            if (retval != PAM_SUCCESS) {
191
 
                _log_err( LOG_NOTICE
192
 
                          , "password - (old) token not obtained" );
193
 
                TALLOC_FREE(sampass);
194
 
                CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
195
 
                return retval;
196
 
            }
197
 
 
198
 
            /* verify that this is the password for this user */
199
 
 
200
 
            retval = _smb_verify_password( pamh, sampass, pass_old, ctrl );
201
 
 
202
 
        } else {
203
 
            pass_old = NULL;
204
 
            retval = PAM_SUCCESS;           /* root doesn't have to */
205
 
        }
206
 
 
207
 
        pass_old = NULL;
208
 
        TALLOC_FREE(sampass);
209
 
        CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
210
 
        return retval;
211
 
 
212
 
    } else if (flags & PAM_UPDATE_AUTHTOK) {
213
 
 
214
 
        /*
215
 
         * obtain the proposed password
216
 
         */
217
 
 
218
 
        /*
219
 
         * get the old token back. NULL was ok only if root [at this
220
 
         * point we assume that this has already been enforced on a
221
 
         * previous call to this function].
222
 
         */
223
 
 
224
 
        if (off( SMB_NOT_SET_PASS, ctrl )) {
225
 
            retval = pam_get_item( pamh, PAM_OLDAUTHTOK,
226
 
                                   (const void **)&pass_old );
227
 
        } else {
228
 
            retval = pam_get_data( pamh, _SMB_OLD_AUTHTOK,
229
 
                                   (const void **)&pass_old );
230
 
            if (retval == PAM_NO_MODULE_DATA) {
231
 
                pass_old = NULL;
232
 
                retval = PAM_SUCCESS;
233
 
            }
234
 
        }
235
 
 
236
 
        if (retval != PAM_SUCCESS) {
237
 
            _log_err( LOG_NOTICE, "password: user not authenticated" );
238
 
            TALLOC_FREE(sampass);
239
 
            CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
240
 
            return retval;
241
 
        }
242
 
 
243
 
        /*
244
 
         * use_authtok is to force the use of a previously entered
245
 
         * password -- needed for pluggable password strength checking
246
 
         * or other module stacking
247
 
         */
248
 
 
249
 
        if (on( SMB_USE_AUTHTOK, ctrl )) {
250
 
            set( SMB_USE_FIRST_PASS, ctrl );
251
 
        }
252
 
 
253
 
        retval = _smb_read_password( pamh, ctrl
254
 
                                      , NULL
255
 
                                      , "Enter new SMB password: "
256
 
                                      , "Retype new SMB password: "
257
 
                                      , _SMB_NEW_AUTHTOK
258
 
                                      , &pass_new );
259
 
 
260
 
        if (retval != PAM_SUCCESS) {
261
 
            if (on( SMB_DEBUG, ctrl )) {
262
 
                _log_err( LOG_ALERT
263
 
                          , "password: new password not obtained" );
264
 
            }
265
 
            pass_old = NULL;                               /* tidy up */
266
 
            TALLOC_FREE(sampass);
267
 
            CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
268
 
            return retval;
269
 
        }
270
 
 
271
 
        /*
272
 
         * At this point we know who the user is and what they
273
 
         * propose as their new password. Verify that the new
274
 
         * password is acceptable.
275
 
         */ 
276
 
 
277
 
        if (pass_new[0] == '\0') {     /* "\0" password = NULL */
278
 
            pass_new = NULL;
279
 
        }
280
 
 
281
 
        retval = _pam_smb_approve_pass(pamh, ctrl, pass_old, pass_new);
282
 
 
283
 
        if (retval != PAM_SUCCESS) {
284
 
            _log_err(LOG_NOTICE, "new password not acceptable");
285
 
            pass_new = pass_old = NULL;               /* tidy up */
286
 
            TALLOC_FREE(sampass);
287
 
            CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
288
 
            return retval;
289
 
        }
290
 
 
291
 
        /*
292
 
         * By reaching here we have approved the passwords and must now
293
 
         * rebuild the smb password file.
294
 
         */
295
 
 
296
 
        /* update the password database */
297
 
 
298
 
        retval = smb_update_db(pamh, ctrl, user, pass_new);
299
 
        if (retval == PAM_SUCCESS) {
300
 
            uid_t uid;
301
 
            
302
 
            /* password updated */
303
 
                if (!sid_to_uid(pdb_get_user_sid(sampass), &uid)) {
304
 
                        _log_err( LOG_NOTICE, "Unable to get uid for user %s",
305
 
                                pdb_get_username(sampass));
306
 
                        _log_err( LOG_NOTICE, "password for (%s) changed by (%s/%d)",
307
 
                                user, uidtoname(getuid()), getuid());
308
 
                } else {
309
 
                        _log_err( LOG_NOTICE, "password for (%s/%d) changed by (%s/%d)",
310
 
                                user, uid, uidtoname(getuid()), getuid());
311
 
                }
312
 
        } else {
313
 
                _log_err( LOG_ERR, "password change failed for user %s", user);
314
 
        }
315
 
 
316
 
        pass_old = pass_new = NULL;
317
 
        if (sampass) {
318
 
                TALLOC_FREE(sampass);
319
 
                sampass = NULL;
320
 
        }
321
 
 
322
 
    } else {            /* something has broken with the library */
323
 
 
324
 
        _log_err( LOG_ALERT, "password received unknown request" );
325
 
        retval = PAM_ABORT;
326
 
 
327
 
    }
328
 
    
329
 
    if (sampass) {
330
 
        TALLOC_FREE(sampass);
331
 
        sampass = NULL;
332
 
    }
333
 
 
334
 
    TALLOC_FREE(sampass);
335
 
    CatchSignal(SIGPIPE, SIGNAL_CAST oldsig_handler);
336
 
    return retval;
337
 
}
338
 
 
339
 
/* static module data */
340
 
#ifdef PAM_STATIC
341
 
struct pam_module _pam_smbpass_passwd_modstruct = {
342
 
     "pam_smbpass",
343
 
     NULL,
344
 
     NULL,
345
 
     NULL,
346
 
     NULL,
347
 
     NULL,
348
 
     pam_sm_chauthtok
349
 
};
350
 
#endif
351