~ubuntu-branches/ubuntu/breezy/pam/breezy

« back to all changes in this revision

Viewing changes to Linux-PAM/modules/pam_unix/pam_unix_passwd.c

  • Committer: Bazaar Package Importer
  • Author(s): Sam Hartman
  • Date: 2004-06-28 14:28:08 UTC
  • mfrom: (2.1.1 warty)
  • Revision ID: james.westby@ubuntu.com-20040628142808-adikk7vtfg3pzcjw
Tags: 0.76-22
* Add uploaders
* Document location of repository
* Fix options containing arguments in pam_unix, Closes: #254904

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Main coding by Elliot Lee <sopwith@redhat.com>, Red Hat Software. 
 
3
 * Copyright (C) 1996.
 
4
 * Copyright (c) Jan R�korajski, 1999.
 
5
 *
 
6
 * Redistribution and use in source and binary forms, with or without
 
7
 * modification, are permitted provided that the following conditions
 
8
 * are met:
 
9
 * 1. Redistributions of source code must retain the above copyright
 
10
 *    notice, and the entire permission notice in its entirety,
 
11
 *    including the disclaimer of warranties.
 
12
 * 2. Redistributions in binary form must reproduce the above copyright
 
13
 *    notice, this list of conditions and the following disclaimer in the
 
14
 *    documentation and/or other materials provided with the distribution.
 
15
 * 3. The name of the author may not be used to endorse or promote
 
16
 *    products derived from this software without specific prior
 
17
 *    written permission.
 
18
 *
 
19
 * ALTERNATIVELY, this product may be distributed under the terms of
 
20
 * the GNU Public License, in which case the provisions of the GPL are
 
21
 * required INSTEAD OF the above restrictions.  (This clause is
 
22
 * necessary due to a potential bad interaction between the GPL and
 
23
 * the restrictions contained in a BSD-style copyright.)
 
24
 *
 
25
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
 
26
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 
27
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 
28
 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
 
29
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 
30
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 
31
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 
32
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 
33
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 
34
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 
35
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 
36
 */
 
37
 
 
38
#include <security/_pam_aconf.h>
 
39
 
 
40
#include <stdio.h>
 
41
#include <stdlib.h>
 
42
#include <stdarg.h>
 
43
#include <string.h>
 
44
#include <malloc.h>
 
45
#include <unistd.h>
 
46
#include <errno.h>
 
47
#include <sys/types.h>
 
48
#include <pwd.h>
 
49
#include <syslog.h>
 
50
#include <shadow.h>
 
51
#include <time.h>               /* for time() */
 
52
#include <fcntl.h>
 
53
#include <ctype.h>
 
54
#include <sys/time.h>
 
55
#include <sys/stat.h>
 
56
#include <rpc/rpc.h>
 
57
#include <rpcsvc/yp_prot.h>
 
58
#include <rpcsvc/ypclnt.h>
 
59
 
 
60
#ifdef USE_CRACKLIB
 
61
#include <crack.h>
 
62
#endif
 
63
 
 
64
#include <security/_pam_macros.h>
 
65
 
 
66
/* indicate the following groups are defined */
 
67
 
 
68
#define PAM_SM_PASSWORD
 
69
 
 
70
#include <security/pam_modules.h>
 
71
 
 
72
#ifndef LINUX_PAM
 
73
#include <security/pam_appl.h>
 
74
#endif                          /* LINUX_PAM */
 
75
 
 
76
#include "yppasswd.h"
 
77
#include "md5.h"
 
78
#include "support.h"
 
79
 
 
80
#if !((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 1))
 
81
extern int getrpcport(const char *host, unsigned long prognum,
 
82
                      unsigned long versnum, unsigned int proto);
 
83
#endif                          /* GNU libc 2.1 */
 
84
 
 
85
/*
 
86
 * PAM framework looks for these entry-points to pass control to the
 
87
 * password changing module.
 
88
 */
 
89
 
 
90
#ifdef NEED_LCKPWDF
 
91
# include "./lckpwdf.-c"
 
92
#endif
 
93
 
 
94
extern char *bigcrypt(const char *key, const char *salt);
 
95
 
 
96
/*
 
97
   How it works:
 
98
   Gets in username (has to be done) from the calling program
 
99
   Does authentication of user (only if we are not running as root)
 
100
   Gets new password/checks for sanity
 
101
   Sets it.
 
102
 */
 
103
 
 
104
/* passwd/salt conversion macros */
 
105
 
 
106
#define ascii_to_bin(c) ((c)>='a'?(c-59):(c)>='A'?((c)-53):(c)-'.')
 
107
#define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.')
 
108
 
 
109
/* data tokens */
 
110
 
 
111
#define _UNIX_OLD_AUTHTOK       "-UN*X-OLD-PASS"
 
112
#define _UNIX_NEW_AUTHTOK       "-UN*X-NEW-PASS"
 
113
 
 
114
#define MAX_PASSWD_TRIES        3
 
115
#define PW_FILE                 "/etc/passwd"
 
116
#define PW_TMPFILE              "/etc/npasswd"
 
117
#define SH_TMPFILE              "/etc/nshadow"
 
118
#define CRACKLIB_DICTS          "/usr/share/dict/cracklib_dict"
 
119
#define OPW_TMPFILE             "/etc/security/nopasswd"
 
120
#define OLD_PASSWORDS_FILE      "/etc/security/opasswd"
 
121
 
 
122
extern const char *obscure_msg(const char *, const char *, const struct passwd *,
 
123
                               unsigned int);
 
124
 
 
125
/*
 
126
 * i64c - convert an integer to a radix 64 character
 
127
 */
 
128
static int i64c(int i)
 
129
{
 
130
        if (i < 0)
 
131
                return ('.');
 
132
        else if (i > 63)
 
133
                return ('z');
 
134
        if (i == 0)
 
135
                return ('.');
 
136
        if (i == 1)
 
137
                return ('/');
 
138
        if (i >= 2 && i <= 11)
 
139
                return ('0' - 2 + i);
 
140
        if (i >= 12 && i <= 37)
 
141
                return ('A' - 12 + i);
 
142
        if (i >= 38 && i <= 63)
 
143
                return ('a' - 38 + i);
 
144
        return ('\0');
 
145
}
 
146
 
 
147
static char *crypt_md5_wrapper(const char *pass_new)
 
148
{
 
149
        /*
 
150
         * Code lifted from Marek Michalkiewicz's shadow suite. (CG)
 
151
         * removed use of static variables (AGM)
 
152
         */
 
153
 
 
154
        struct timeval tv;
 
155
        MD5_CTX ctx;
 
156
        unsigned char result[16];
 
157
        char *cp = (char *) result;
 
158
        unsigned char tmp[16];
 
159
        int i;
 
160
        char *x = NULL;
 
161
 
 
162
        GoodMD5Init(&ctx);
 
163
        gettimeofday(&tv, (struct timezone *) 0);
 
164
        GoodMD5Update(&ctx, (void *) &tv, sizeof tv);
 
165
        i = getpid();
 
166
        GoodMD5Update(&ctx, (void *) &i, sizeof i);
 
167
        i = clock();
 
168
        GoodMD5Update(&ctx, (void *) &i, sizeof i);
 
169
        GoodMD5Update(&ctx, result, sizeof result);
 
170
        GoodMD5Final(tmp, &ctx);
 
171
        strcpy(cp, "$1$");      /* magic for the MD5 */
 
172
        cp += strlen(cp);
 
173
        for (i = 0; i < 8; i++)
 
174
                *cp++ = i64c(tmp[i] & 077);
 
175
        *cp = '\0';
 
176
 
 
177
        /* no longer need cleartext */
 
178
        x = Goodcrypt_md5(pass_new, (const char *) result);
 
179
 
 
180
        return x;
 
181
}
 
182
 
 
183
static char *getNISserver(pam_handle_t *pamh)
 
184
{
 
185
        char *master;
 
186
        char *domainname;
 
187
        int port, err;
 
188
 
 
189
        if ((err = yp_get_default_domain(&domainname)) != 0) {
 
190
                _log_err(LOG_WARNING, pamh, "can't get local yp domain: %s\n",
 
191
                         yperr_string(err));
 
192
                return NULL;
 
193
        }
 
194
        if ((err = yp_master(domainname, "passwd.byname", &master)) != 0) {
 
195
                _log_err(LOG_WARNING, pamh, "can't find the master ypserver: %s\n",
 
196
                         yperr_string(err));
 
197
                return NULL;
 
198
        }
 
199
        port = getrpcport(master, YPPASSWDPROG, YPPASSWDPROC_UPDATE, IPPROTO_UDP);
 
200
        if (port == 0) {
 
201
                _log_err(LOG_WARNING, pamh,
 
202
                         "yppasswdd not running on NIS master host\n");
 
203
                return NULL;
 
204
        }
 
205
        if (port >= IPPORT_RESERVED) {
 
206
                _log_err(LOG_WARNING, pamh,
 
207
                         "yppasswd daemon running on illegal port.\n");
 
208
                return NULL;
 
209
        }
 
210
        return master;
 
211
}
 
212
 
 
213
static struct passwd *_unix_getpwnam(const char *name)
 
214
{
 
215
        struct passwd *ent = NULL;
 
216
        FILE *pwfile;
 
217
 
 
218
        pwfile = fopen(PW_FILE, "r");
 
219
        if (pwfile != NULL) {
 
220
                ent = fgetpwent(pwfile);
 
221
                while (ent && (strcmp(ent->pw_name, name) != 0))
 
222
                        ent = fgetpwent(pwfile);
 
223
                fclose(pwfile);
 
224
        }
 
225
        return ent;
 
226
}
 
227
 
 
228
static int check_old_password(const char *forwho, const char *newpass)
 
229
{
 
230
        static char buf[16384];
 
231
        char *s_luser, *s_uid, *s_npas, *s_pas;
 
232
        int retval = PAM_SUCCESS;
 
233
        FILE *opwfile;
 
234
 
 
235
        opwfile = fopen(OLD_PASSWORDS_FILE, "r");
 
236
        if (opwfile == NULL)
 
237
                return PAM_AUTHTOK_ERR;
 
238
 
 
239
        while (fgets(buf, 16380, opwfile)) {
 
240
                if (!strncmp(buf, forwho, strlen(forwho))) {
 
241
                        buf[strlen(buf) - 1] = '\0';
 
242
                        s_luser = strtok(buf, ":,");
 
243
                        s_uid = strtok(NULL, ":,");
 
244
                        s_npas = strtok(NULL, ":,");
 
245
                        s_pas = strtok(NULL, ":,");
 
246
                        while (s_pas != NULL) {
 
247
                                char *md5pass = Goodcrypt_md5(newpass, s_pas);
 
248
                                if (!strcmp(md5pass, s_pas)) {
 
249
                                        _pam_delete(md5pass);
 
250
                                        retval = PAM_AUTHTOK_ERR;
 
251
                                        break;
 
252
                                }
 
253
                                s_pas = strtok(NULL, ":,");
 
254
                                _pam_delete(md5pass);
 
255
                        }
 
256
                        break;
 
257
                }
 
258
        }
 
259
        fclose(opwfile);
 
260
 
 
261
        return retval;
 
262
}
 
263
 
 
264
static int save_old_password(const char *forwho, const char *oldpass,
 
265
                             int howmany)
 
266
{
 
267
    static char buf[16384];
 
268
    static char nbuf[16384];
 
269
    char *s_luser, *s_uid, *s_npas, *s_pas, *pass;
 
270
    int npas;
 
271
    FILE *pwfile, *opwfile;
 
272
    int err = 0;
 
273
    int oldmask;
 
274
    int found = 0;
 
275
    struct passwd *pwd = NULL;
 
276
 
 
277
    if (howmany < 0) {
 
278
        return PAM_SUCCESS;
 
279
    }
 
280
 
 
281
    if (oldpass == NULL) {
 
282
        return PAM_SUCCESS;
 
283
    }
 
284
 
 
285
    oldmask = umask(077);
 
286
    pwfile = fopen(OPW_TMPFILE, "w");
 
287
    umask(oldmask);
 
288
    if (pwfile == NULL) {
 
289
        return PAM_AUTHTOK_ERR;
 
290
    }
 
291
 
 
292
    opwfile = fopen(OLD_PASSWORDS_FILE, "r");
 
293
    if (opwfile == NULL) {
 
294
        fclose(pwfile);
 
295
        return PAM_AUTHTOK_ERR;
 
296
    }
 
297
 
 
298
    chown(OPW_TMPFILE, 0, 0);
 
299
    chmod(OPW_TMPFILE, 0600);
 
300
 
 
301
    while (fgets(buf, 16380, opwfile)) {
 
302
        if (!strncmp(buf, forwho, strlen(forwho))) {
 
303
            buf[strlen(buf) - 1] = '\0';
 
304
            s_luser = strtok(buf, ":");
 
305
            s_uid = strtok(NULL, ":");
 
306
            s_npas = strtok(NULL, ":");
 
307
            s_pas = strtok(NULL, ":");
 
308
            npas = strtol(s_npas, NULL, 10) + 1;
 
309
            while (npas > howmany) {
 
310
                s_pas = strpbrk(s_pas, ",");
 
311
                if (s_pas != NULL)
 
312
                    s_pas++;
 
313
                npas--;
 
314
            }
 
315
            pass = crypt_md5_wrapper(oldpass);
 
316
            if (s_pas == NULL)
 
317
                snprintf(nbuf, sizeof(nbuf), "%s:%s:%d:%s\n",
 
318
                         s_luser, s_uid, npas, pass);
 
319
            else
 
320
                snprintf(nbuf, sizeof(nbuf),"%s:%s:%d:%s,%s\n",
 
321
                         s_luser, s_uid, npas, s_pas, pass);
 
322
            _pam_delete(pass);
 
323
            if (fputs(nbuf, pwfile) < 0) {
 
324
                err = 1;
 
325
                break;
 
326
            }
 
327
            found = 1;
 
328
        } else if (fputs(buf, pwfile) < 0) {
 
329
            err = 1;
 
330
            break;
 
331
        }
 
332
    }
 
333
    fclose(opwfile);
 
334
 
 
335
    if (!found) {
 
336
        pwd = _unix_getpwnam(forwho);
 
337
        if (pwd == NULL) {
 
338
            err = 1;
 
339
        } else {
 
340
            pass = crypt_md5_wrapper(oldpass);
 
341
            snprintf(nbuf, sizeof(nbuf), "%s:%d:1:%s\n",
 
342
                     forwho, pwd->pw_uid, pass);
 
343
            _pam_delete(pass);
 
344
            if (fputs(nbuf, pwfile) < 0) {
 
345
                err = 1;
 
346
            }
 
347
        }
 
348
    }
 
349
 
 
350
    if (fclose(pwfile)) {
 
351
        D(("error writing entries to old passwords file: %s\n",
 
352
           strerror(errno)));
 
353
        err = 1;
 
354
    }
 
355
 
 
356
    if (!err) {
 
357
        rename(OPW_TMPFILE, OLD_PASSWORDS_FILE);
 
358
        return PAM_SUCCESS;
 
359
    } else {
 
360
        unlink(OPW_TMPFILE);
 
361
        return PAM_AUTHTOK_ERR;
 
362
    }
 
363
}
 
364
 
 
365
static int _update_passwd(pam_handle_t *pamh,
 
366
                          const char *forwho, const char *towhat)
 
367
{
 
368
    struct passwd *tmpent = NULL;
 
369
    FILE *pwfile, *opwfile;
 
370
    int err = 1, found = 0;
 
371
    int oldmask;
 
372
 
 
373
    oldmask = umask(077);
 
374
    pwfile = fopen(PW_TMPFILE, "w");
 
375
    umask(oldmask);
 
376
    if (pwfile == NULL) {
 
377
        return PAM_AUTHTOK_ERR;
 
378
    }
 
379
 
 
380
    opwfile = fopen("/etc/passwd", "r");
 
381
    if (opwfile == NULL) {
 
382
        fclose(pwfile);
 
383
        return PAM_AUTHTOK_ERR;
 
384
    }
 
385
 
 
386
    chown(PW_TMPFILE, 0, 0);
 
387
    chmod(PW_TMPFILE, 0644);
 
388
    tmpent = fgetpwent(opwfile);
 
389
    while (tmpent) {
 
390
        if (!strcmp(tmpent->pw_name, forwho)) {
 
391
            /* To shut gcc up */
 
392
            union {
 
393
                const char *const_charp;
 
394
                char *charp;
 
395
            } assigned_passwd;
 
396
            assigned_passwd.const_charp = towhat;
 
397
                        
 
398
            tmpent->pw_passwd = assigned_passwd.charp;
 
399
            err = 0;
 
400
            found = 1;
 
401
        }
 
402
        if (putpwent(tmpent, pwfile)) {
 
403
            D(("error writing entry to password file: %s\n", strerror(errno)));
 
404
            err = 1;
 
405
            break;
 
406
        }
 
407
        tmpent = fgetpwent(opwfile);
 
408
    }
 
409
    fclose(opwfile);
 
410
 
 
411
    if (fclose(pwfile)) {
 
412
        D(("error writing entries to password file: %s\n", strerror(errno)));
 
413
        err = 1;
 
414
    }
 
415
    
 
416
    if (!err) {
 
417
        rename(PW_TMPFILE, "/etc/passwd");
 
418
        _log_err(LOG_NOTICE, pamh, "password changed for %s", forwho);
 
419
        return PAM_SUCCESS;
 
420
    } else {
 
421
        unlink(PW_TMPFILE);
 
422
        return found?PAM_AUTHTOK_ERR:PAM_USER_UNKNOWN;
 
423
    }
 
424
}
 
425
 
 
426
static int _update_shadow(const char *forwho, char *towhat)
 
427
{
 
428
    struct spwd *spwdent = NULL, *stmpent = NULL;
 
429
    FILE *pwfile, *opwfile;
 
430
    int err = 1, found = 0;
 
431
    int oldmask;
 
432
 
 
433
    spwdent = getspnam(forwho);
 
434
    if (spwdent == NULL) {
 
435
        return PAM_USER_UNKNOWN;
 
436
    }
 
437
    oldmask = umask(077);
 
438
    pwfile = fopen(SH_TMPFILE, "w");
 
439
    umask(oldmask);
 
440
    if (pwfile == NULL) {
 
441
        return PAM_AUTHTOK_ERR;
 
442
    }
 
443
 
 
444
    opwfile = fopen("/etc/shadow", "r");
 
445
    if (opwfile == NULL) {
 
446
        fclose(pwfile);
 
447
        return PAM_AUTHTOK_ERR;
 
448
    }
 
449
 
 
450
        /* Debian's shadow file is group shadow, and readable by group,
 
451
           shouldn't all of them be this way? */
 
452
        chown(SH_TMPFILE, 0, 42);
 
453
        chmod(SH_TMPFILE, 0640);
 
454
    stmpent = fgetspent(opwfile);
 
455
    while (stmpent) {
 
456
 
 
457
        if (!strcmp(stmpent->sp_namp, forwho)) {
 
458
            stmpent->sp_pwdp = towhat;
 
459
            stmpent->sp_lstchg = time(NULL) / (60 * 60 * 24);
 
460
            err = 0;
 
461
            found = 1;
 
462
            D(("Set password %s for %s", stmpent->sp_pwdp, forwho));
 
463
        }
 
464
 
 
465
        if (putspent(stmpent, pwfile)) {
 
466
            D(("error writing entry to shadow file: %s\n", strerror(errno)));
 
467
            err = 1;
 
468
            break;
 
469
        }
 
470
 
 
471
        stmpent = fgetspent(opwfile);
 
472
    }
 
473
    fclose(opwfile);
 
474
 
 
475
    if (fclose(pwfile)) {
 
476
        D(("error writing entries to shadow file: %s\n", strerror(errno)));
 
477
        err = 1;
 
478
    }
 
479
 
 
480
    if (!err) {
 
481
        rename(SH_TMPFILE, "/etc/shadow");
 
482
        return PAM_SUCCESS;
 
483
    } else {
 
484
        unlink(SH_TMPFILE);
 
485
        return found?PAM_AUTHTOK_ERR:PAM_USER_UNKNOWN;
 
486
    }
 
487
}
 
488
 
 
489
static int _do_setpass(pam_handle_t* pamh, const char *forwho, char *fromwhat,
 
490
                       char *towhat, unsigned int ctrl, int remember)
 
491
{
 
492
        struct passwd *pwd = NULL;
 
493
        int retval = 0;
 
494
 
 
495
        D(("called"));
 
496
 
 
497
        pwd = getpwnam(forwho);
 
498
        if (pwd == NULL)
 
499
                return PAM_AUTHTOK_ERR;
 
500
 
 
501
        if (on(UNIX_NIS, ctrl)) {
 
502
                struct timeval timeout;
 
503
                struct yppasswd yppwd;
 
504
                CLIENT *clnt;
 
505
                char *master;
 
506
                int status;
 
507
                int err = 0;
 
508
 
 
509
                /* Make RPC call to NIS server */
 
510
                if ((master = getNISserver(pamh)) == NULL)
 
511
                        return PAM_TRY_AGAIN;
 
512
 
 
513
                /* Initialize password information */
 
514
                yppwd.newpw.pw_passwd = pwd->pw_passwd;
 
515
                yppwd.newpw.pw_name = pwd->pw_name;
 
516
                yppwd.newpw.pw_uid = pwd->pw_uid;
 
517
                yppwd.newpw.pw_gid = pwd->pw_gid;
 
518
                yppwd.newpw.pw_gecos = pwd->pw_gecos;
 
519
                yppwd.newpw.pw_dir = pwd->pw_dir;
 
520
                yppwd.newpw.pw_shell = pwd->pw_shell;
 
521
                yppwd.oldpass = fromwhat ? fromwhat : "";
 
522
                yppwd.newpw.pw_passwd = towhat;
 
523
 
 
524
                D(("Set password %s for %s", yppwd.newpw.pw_passwd, forwho));
 
525
 
 
526
                /* The yppasswd.x file said `unix authentication required',
 
527
                 * so I added it. This is the only reason it is in here.
 
528
                 * My yppasswdd doesn't use it, but maybe some others out there
 
529
                 * do.                                        --okir
 
530
                 */
 
531
                clnt = clnt_create(master, YPPASSWDPROG, YPPASSWDVERS, "udp");
 
532
                clnt->cl_auth = authunix_create_default();
 
533
                memset((char *) &status, '\0', sizeof(status));
 
534
                timeout.tv_sec = 25;
 
535
                timeout.tv_usec = 0;
 
536
                err = clnt_call(clnt, YPPASSWDPROC_UPDATE,
 
537
                                (xdrproc_t) xdr_yppasswd, (char *) &yppwd,
 
538
                                (xdrproc_t) xdr_int, (char *) &status,
 
539
                                timeout);
 
540
 
 
541
                if (err) {
 
542
                        clnt_perrno(err);
 
543
                        retval = PAM_TRY_AGAIN;
 
544
                } else if (status) {
 
545
                        D(("Error while changing NIS password.\n"));
 
546
                        retval = PAM_TRY_AGAIN;
 
547
                }
 
548
                D(("The password has%s been changed on %s.",
 
549
                   (err || status) ? " not" : "", master));
 
550
                _log_err(LOG_NOTICE, pamh, "password%s changed for %s on %s",
 
551
                         (err || status) ? " not" : "", pwd->pw_name, master);
 
552
 
 
553
                auth_destroy(clnt->cl_auth);
 
554
                clnt_destroy(clnt);
 
555
                if ((err || status) != 0) {
 
556
                        retval = PAM_TRY_AGAIN;
 
557
                }
 
558
#ifdef DEBUG
 
559
                sleep(5);
 
560
#endif
 
561
                _log_err(LOG_NOTICE, pamh, "NIS Password for %s was changed on %s", forwho, master);
 
562
 
 
563
                return retval;
 
564
        }
 
565
        /* first, save old password */
 
566
        if (save_old_password(forwho, fromwhat, remember)) {
 
567
                return PAM_AUTHTOK_ERR;
 
568
        }
 
569
 
 
570
#ifdef USE_LCKPWDF
 
571
        /*
 
572
         * These values for the number of attempts and the sleep time
 
573
         * are, of course, completely arbitrary.
 
574
         *
 
575
         * My reading of the PAM docs is that, once pam_chauthtok()
 
576
         * has been called with PAM_UPDATE_AUTHTOK, we are obliged to
 
577
         * take any reasonable steps to make sure the token is
 
578
         * updated; so retrying for 1/10 sec. isn't overdoing it.
 
579
         */
 
580
 
 
581
        retval = lckpwdf();
 
582
        if (retval != 0) {
 
583
            return PAM_AUTHTOK_LOCK_BUSY;
 
584
        }
 
585
#endif /* def USE_LCKPWDF */
 
586
        
 
587
        if (on(UNIX_SHADOW, ctrl) || (strcmp(pwd->pw_passwd, "x") == 0)) {
 
588
                retval = _update_shadow(forwho, towhat);
 
589
                if (retval == PAM_SUCCESS)
 
590
                        retval = _update_passwd(pamh, forwho, "x");
 
591
        } else {
 
592
                retval = _update_passwd(pamh, forwho, towhat);
 
593
        }
 
594
 
 
595
        if (retval == PAM_SUCCESS)
 
596
            _log_err(LOG_NOTICE, pamh, "Password for %s was changed", forwho);
 
597
 
 
598
#ifdef USE_LCKPWDF
 
599
        ulckpwdf();
 
600
#endif /* def USE_LCKPWDF */
 
601
 
 
602
        return retval;
 
603
}
 
604
 
 
605
static int _unix_verify_shadow(const char *user, unsigned int ctrl)
 
606
{
 
607
        struct passwd *pwd = NULL;      /* Password and shadow password */
 
608
        struct spwd *spwdent = NULL;    /* file entries for the user */
 
609
        time_t curdays;
 
610
        int retval = PAM_SUCCESS;
 
611
 
 
612
        /* UNIX passwords area */
 
613
        pwd = _unix_getpwnam(user);     /* Get password *file* entry... */
 
614
        if (pwd == NULL)
 
615
                return PAM_AUTHINFO_UNAVAIL;    /* We don't need to do the rest... */
 
616
 
 
617
        if (strcmp(pwd->pw_passwd, "x") == 0) {
 
618
                /* ...and shadow password file entry for this user, if shadowing
 
619
                   is enabled */
 
620
                setspent();
 
621
                spwdent = getspnam(user);
 
622
                endspent();
 
623
 
 
624
                if (spwdent == NULL)
 
625
                        return PAM_AUTHINFO_UNAVAIL;
 
626
        } else {
 
627
                if (strcmp(pwd->pw_passwd,"*NP*") == 0) { /* NIS+ */                 
 
628
                        uid_t save_uid;
 
629
 
 
630
                        save_uid = geteuid();
 
631
                        seteuid (pwd->pw_uid);
 
632
                        spwdent = getspnam( user );
 
633
                        seteuid (save_uid);
 
634
 
 
635
                        if (spwdent == NULL)
 
636
                                return PAM_AUTHINFO_UNAVAIL;
 
637
                } else
 
638
                        spwdent = NULL;
 
639
        }
 
640
 
 
641
        if (spwdent != NULL) {
 
642
                /* We have the user's information, now let's check if their account
 
643
                   has expired (60 * 60 * 24 = number of seconds in a day) */
 
644
 
 
645
                if (off(UNIX__IAMROOT, ctrl)) {
 
646
                        /* Get the current number of days since 1970 */
 
647
                        curdays = time(NULL) / (60 * 60 * 24);
 
648
                        if ((curdays < (spwdent->sp_lstchg + spwdent->sp_min))
 
649
                            && (spwdent->sp_min != -1))
 
650
                                retval = PAM_AUTHTOK_ERR;
 
651
                        else if ((curdays > (spwdent->sp_lstchg + spwdent->sp_max + spwdent->sp_inact))
 
652
                                 && (spwdent->sp_max != -1) && (spwdent->sp_inact != -1)
 
653
                                 && (spwdent->sp_lstchg != 0))
 
654
                                /*
 
655
                                 * Their password change has been put off too long,
 
656
                                 */
 
657
                                retval = PAM_ACCT_EXPIRED;
 
658
                        else if ((curdays > spwdent->sp_expire) && (spwdent->sp_expire != -1)
 
659
                                 && (spwdent->sp_lstchg != 0))
 
660
                                /*
 
661
                                 * OR their account has just plain expired
 
662
                                 */
 
663
                                retval = PAM_ACCT_EXPIRED;
 
664
                }
 
665
        }
 
666
        return retval;
 
667
}
 
668
 
 
669
static int _pam_unix_approve_pass(pam_handle_t * pamh
 
670
                                  ,unsigned int ctrl
 
671
                                  ,const char *pass_old
 
672
                                  ,const char *pass_new)
 
673
{
 
674
        const char *user;
 
675
        const char *remark = NULL;
 
676
        int retval = PAM_SUCCESS;
 
677
 
 
678
        D(("&new=%p, &old=%p", pass_old, pass_new));
 
679
        D(("new=[%s]", pass_new));
 
680
        D(("old=[%s]", pass_old));
 
681
 
 
682
        if (pass_new == NULL || (pass_old && !strcmp(pass_old, pass_new))) {
 
683
                if (on(UNIX_DEBUG, ctrl)) {
 
684
                        _log_err(LOG_DEBUG, pamh, "bad authentication token");
 
685
                }
 
686
                _make_remark(pamh, ctrl, PAM_ERROR_MSG, pass_new == NULL ?
 
687
                          "No password supplied" : "Password unchanged");
 
688
                return PAM_AUTHTOK_ERR;
 
689
        }
 
690
        /*
 
691
         * if one wanted to hardwire authentication token strength
 
692
         * checking this would be the place - AGM
 
693
         */
 
694
 
 
695
        retval = pam_get_item(pamh, PAM_USER, (const void **) &user);
 
696
        if (retval != PAM_SUCCESS) {
 
697
                if (on(UNIX_DEBUG, ctrl)) {
 
698
                        _log_err(LOG_ERR, pamh, "Can not get username");
 
699
                        return PAM_AUTHTOK_ERR;
 
700
                }
 
701
        }
 
702
 
 
703
        if (off(UNIX__IAMROOT, ctrl)) {
 
704
#ifdef USE_CRACKLIB
 
705
                remark = FascistCheck(pass_new, CRACKLIB_DICTS);
 
706
                D(("called cracklib [%s]", remark));
 
707
#endif
 
708
                if (!remark && on(UNIX_REMEMBER_PASSWD, ctrl))
 
709
                        if ((retval = check_old_password(user, pass_new)) != PAM_SUCCESS)
 
710
                                remark = "Password has been already used. Choose another.";
 
711
 
 
712
                if (!remark && pass_old != NULL) { /* only check if we don't already have a failure */
 
713
                    struct passwd *pwd;
 
714
                    pwd = getpwnam(user);
 
715
                    remark = (char *)obscure_msg(pass_old,pass_new,pwd,ctrl); /* do obscure checks */
 
716
                }
 
717
        }
 
718
        if (remark) {
 
719
                _make_remark(pamh, ctrl, PAM_ERROR_MSG, remark);
 
720
                retval = PAM_AUTHTOK_ERR;
 
721
        }
 
722
        return retval;
 
723
}
 
724
 
 
725
 
 
726
PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
 
727
                                int argc, const char **argv)
 
728
{
 
729
        unsigned int ctrl, lctrl;
 
730
        int retval;
 
731
        int remember = -1;
 
732
 
 
733
        /* <DO NOT free() THESE> */
 
734
        const char *user;
 
735
        char *pass_old, *pass_new;
 
736
        /* </DO NOT free() THESE> */
 
737
 
 
738
        D(("called."));
 
739
 
 
740
        ctrl = _set_ctrl(pamh, flags, &remember, argc, argv);
 
741
 
 
742
        /*
 
743
         * First get the name of a user
 
744
         */
 
745
        retval = pam_get_user(pamh, &user, NULL);
 
746
        if (retval == PAM_SUCCESS) {
 
747
                /*
 
748
                 * Various libraries at various times have had bugs related to
 
749
                 * '+' or '-' as the first character of a user name. Don't take
 
750
                 * any chances here. Require that the username starts with an
 
751
                 * alphanumeric character.
 
752
                 */
 
753
                if (user == NULL || !isalnum(*user)) {
 
754
                        _log_err(LOG_ERR, pamh, "bad username [%s]", user);
 
755
                        return PAM_USER_UNKNOWN;
 
756
                }
 
757
                if (retval == PAM_SUCCESS && on(UNIX_DEBUG, ctrl))
 
758
                        _log_err(LOG_DEBUG, pamh, "username [%s] obtained",
 
759
                                 user);
 
760
        } else {
 
761
                if (on(UNIX_DEBUG, ctrl))
 
762
                        _log_err(LOG_DEBUG, pamh,
 
763
                                 "password - could not identify user");
 
764
                return retval;
 
765
        }
 
766
 
 
767
        D(("Got username of %s", user));
 
768
 
 
769
        /*
 
770
         * This is not an AUTH module!
 
771
         */
 
772
        if (on(UNIX__NONULL, ctrl))
 
773
                set(UNIX__NULLOK, ctrl);
 
774
 
 
775
        if (on(UNIX__PRELIM, ctrl)) {
 
776
                /*
 
777
                 * obtain and verify the current password (OLDAUTHTOK) for
 
778
                 * the user.
 
779
                 */
 
780
                char *Announce;
 
781
 
 
782
                D(("prelim check"));
 
783
 
 
784
                if (_unix_blankpasswd(ctrl, pamh, user)) {
 
785
                        return PAM_SUCCESS;
 
786
                } else if (off(UNIX__IAMROOT, ctrl) || on(UNIX_NIS, ctrl)) {
 
787
 
 
788
                        /* instruct user what is happening */
 
789
#define greeting "Changing password for "
 
790
                        Announce = (char *) malloc(sizeof(greeting) + strlen(user));
 
791
                        if (Announce == NULL) {
 
792
                                _log_err(LOG_CRIT, pamh,
 
793
                                         "password - out of memory");
 
794
                                return PAM_BUF_ERR;
 
795
                        }
 
796
                        (void) strcpy(Announce, greeting);
 
797
                        (void) strcpy(Announce + sizeof(greeting) - 1, user);
 
798
#undef greeting
 
799
 
 
800
                        lctrl = ctrl;
 
801
                        set(UNIX__OLD_PASSWD, lctrl);
 
802
                        retval = _unix_read_password(pamh, lctrl
 
803
                                                     ,Announce
 
804
                                             ,(on(UNIX__IAMROOT, ctrl) 
 
805
                                               ? "NIS server root password: " 
 
806
                                               : "(current) UNIX password: ")
 
807
                                                     ,NULL
 
808
                                                     ,_UNIX_OLD_AUTHTOK
 
809
                                             ,(const char **) &pass_old);
 
810
                        free(Announce);
 
811
 
 
812
                        if (retval != PAM_SUCCESS) {
 
813
                                _log_err(LOG_NOTICE, pamh
 
814
                                 ,"password - (old) token not obtained");
 
815
                                return retval;
 
816
                        }
 
817
                        /* verify that this is the password for this user
 
818
                         * if we're not using NIS */
 
819
 
 
820
                        if (off(UNIX_NIS, ctrl)) {
 
821
                                retval = _unix_verify_password(pamh, user, pass_old, ctrl);
 
822
                        }
 
823
                } else {
 
824
                        D(("process run by root so do nothing this time around"));
 
825
                        pass_old = NULL;
 
826
                        retval = PAM_SUCCESS;   /* root doesn't have too */
 
827
                }
 
828
 
 
829
                if (retval != PAM_SUCCESS) {
 
830
                        D(("Authentication failed"));
 
831
                        pass_old = NULL;
 
832
                        return retval;
 
833
                }
 
834
                retval = pam_set_item(pamh, PAM_OLDAUTHTOK, (const void *) pass_old);
 
835
                pass_old = NULL;
 
836
                if (retval != PAM_SUCCESS) {
 
837
                        _log_err(LOG_CRIT, pamh,
 
838
                                 "failed to set PAM_OLDAUTHTOK");
 
839
                }
 
840
                retval = _unix_verify_shadow(user, ctrl);
 
841
                if (retval == PAM_AUTHTOK_ERR) {
 
842
                        if (off(UNIX__IAMROOT, ctrl))
 
843
                                _make_remark(pamh, ctrl, PAM_ERROR_MSG,
 
844
                                            "You must wait longer to change your password");
 
845
                        else
 
846
                                retval = PAM_SUCCESS;
 
847
                }
 
848
        } else if (on(UNIX__UPDATE, ctrl)) {
 
849
                /*
 
850
                 * tpass is used below to store the _pam_md() return; it
 
851
                 * should be _pam_delete()'d.
 
852
                 */
 
853
 
 
854
                char *tpass = NULL;
 
855
                int retry = 0;
 
856
 
 
857
                /*
 
858
                 * obtain the proposed password
 
859
                 */
 
860
 
 
861
                D(("do update"));
 
862
 
 
863
                /*
 
864
                 * get the old token back. NULL was ok only if root [at this
 
865
                 * point we assume that this has already been enforced on a
 
866
                 * previous call to this function].
 
867
                 */
 
868
 
 
869
                if (off(UNIX_NOT_SET_PASS, ctrl)) {
 
870
                        retval = pam_get_item(pamh, PAM_OLDAUTHTOK
 
871
                                              ,(const void **) &pass_old);
 
872
                } else {
 
873
                        retval = pam_get_data(pamh, _UNIX_OLD_AUTHTOK
 
874
                                              ,(const void **) &pass_old);
 
875
                        if (retval == PAM_NO_MODULE_DATA) {
 
876
                                retval = PAM_SUCCESS;
 
877
                                pass_old = NULL;
 
878
                        }
 
879
                }
 
880
                D(("pass_old [%s]", pass_old));
 
881
 
 
882
                if (retval != PAM_SUCCESS) {
 
883
                        _log_err(LOG_NOTICE, pamh, "user not authenticated");
 
884
                        return retval;
 
885
                }
 
886
                retval = _unix_verify_shadow(user, ctrl);
 
887
                if (retval != PAM_SUCCESS) {
 
888
                        _log_err(LOG_NOTICE, pamh, "user not authenticated 2");
 
889
                        return retval;
 
890
                }
 
891
                D(("get new password now"));
 
892
 
 
893
                lctrl = ctrl;
 
894
 
 
895
                if (on(UNIX_USE_AUTHTOK, lctrl)) {
 
896
                        set(UNIX_USE_FIRST_PASS, lctrl);
 
897
                }
 
898
                retry = 0;
 
899
                retval = PAM_AUTHTOK_ERR;
 
900
                while ((retval != PAM_SUCCESS) && (retry++ < MAX_PASSWD_TRIES)) {
 
901
                        /*
 
902
                         * use_authtok is to force the use of a previously entered
 
903
                         * password -- needed for pluggable password strength checking
 
904
                         */
 
905
 
 
906
                        retval = _unix_read_password(pamh, lctrl
 
907
                                                     ,NULL
 
908
                                             ,"Enter new UNIX password: "
 
909
                                            ,"Retype new UNIX password: "
 
910
                                                     ,_UNIX_NEW_AUTHTOK
 
911
                                             ,(const char **) &pass_new);
 
912
 
 
913
                        if (retval != PAM_SUCCESS) {
 
914
                                if (on(UNIX_DEBUG, ctrl)) {
 
915
                                        _log_err(LOG_ALERT, pamh
 
916
                                                 ,"password - new password not obtained");
 
917
                                }
 
918
                                pass_old = NULL;        /* tidy up */
 
919
                                return retval;
 
920
                        }
 
921
                        D(("returned to _unix_chauthtok"));
 
922
 
 
923
                        /*
 
924
                         * At this point we know who the user is and what they
 
925
                         * propose as their new password. Verify that the new
 
926
                         * password is acceptable.
 
927
                         */
 
928
 
 
929
                        if (pass_new[0] == '\0') {      /* "\0" password = NULL */
 
930
                                pass_new = NULL;
 
931
                        }
 
932
                        retval = _pam_unix_approve_pass(pamh, ctrl, pass_old, pass_new);
 
933
                }
 
934
 
 
935
                if (retval != PAM_SUCCESS) {
 
936
                        _log_err(LOG_NOTICE, pamh,
 
937
                                 "new password not acceptable");
 
938
                        pass_new = pass_old = NULL;     /* tidy up */
 
939
                        return retval;
 
940
                }
 
941
                /*
 
942
                 * By reaching here we have approved the passwords and must now
 
943
                 * rebuild the password database file.
 
944
                 */
 
945
 
 
946
                /*
 
947
                 * First we encrypt the new password.
 
948
                 */
 
949
 
 
950
                if (on(UNIX_MD5_PASS, ctrl)) {
 
951
                        tpass = crypt_md5_wrapper(pass_new);
 
952
                } else {
 
953
                        /*
 
954
                         * Salt manipulation is stolen from Rick Faith's passwd
 
955
                         * program.  Sorry Rick :) -- alex
 
956
                         */
 
957
 
 
958
                        time_t tm;
 
959
                        char salt[3];
 
960
 
 
961
                        time(&tm);
 
962
                        salt[0] = bin_to_ascii(tm & 0x3f);
 
963
                        salt[1] = bin_to_ascii((tm >> 6) & 0x3f);
 
964
                        salt[2] = '\0';
 
965
 
 
966
                        if (off(UNIX_BIGCRYPT, ctrl) && strlen(pass_new) > 8) {
 
967
                                /* 
 
968
                                 * to avoid using the _extensions_ of the bigcrypt()
 
969
                                 * function we truncate the newly entered password
 
970
                                 * [Problems that followed from this are fixed as per
 
971
                                 *  Bug 521314.]
 
972
                                 */
 
973
                                char *temp = malloc(9);
 
974
 
 
975
                                if (temp == NULL) {
 
976
                                        _log_err(LOG_CRIT, pamh,
 
977
                                                 "out of memory for password");
 
978
                                        pass_new = pass_old = NULL;     /* tidy up */
 
979
                                        return PAM_BUF_ERR;
 
980
                                }
 
981
                                /* copy first 8 bytes of password */
 
982
                                strncpy(temp, pass_new, 8);
 
983
                                temp[8] = '\0';
 
984
 
 
985
                                /* no longer need cleartext */
 
986
                                tpass = bigcrypt(temp, salt);
 
987
 
 
988
                                _pam_delete(temp);      /* tidy up */
 
989
                        } else {
 
990
                                tpass = bigcrypt(pass_new, salt);
 
991
                        }
 
992
                }
 
993
 
 
994
                D(("password processed"));
 
995
 
 
996
                /* update the password database(s) -- race conditions..? */
 
997
 
 
998
                retval = _do_setpass(pamh, user, pass_old, tpass, ctrl,
 
999
                                     remember);
 
1000
 
 
1001
                _pam_delete(tpass);
 
1002
                pass_old = pass_new = NULL;
 
1003
        } else {                /* something has broken with the module */
 
1004
                _log_err(LOG_ALERT, pamh,
 
1005
                         "password received unknown request");
 
1006
                retval = PAM_ABORT;
 
1007
        }
 
1008
 
 
1009
        D(("retval was %d", retval));
 
1010
 
 
1011
        return retval;
 
1012
}
 
1013
 
 
1014
 
 
1015
/* static module data */
 
1016
#ifdef PAM_STATIC
 
1017
struct pam_module _pam_unix_passwd_modstruct = {
 
1018
    "pam_unix_passwd",
 
1019
    NULL,
 
1020
    NULL,
 
1021
    NULL,
 
1022
    NULL,
 
1023
    NULL,
 
1024
    pam_sm_chauthtok,
 
1025
};
 
1026
#endif
 
1027