1
0
Files
2022-09-29 17:59:04 +03:00

2580 lines
62 KiB
C

/* Copyright (c) 1990, 1991 UNIX System Laboratories, Inc. */
/* Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T */
/* All Rights Reserved */
/* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF */
/* UNIX System Laboratories, Inc. */
/* The copyright notice above does not evidence any */
/* actual or intended publication of such source code. */
#ident "@(#)passwd:passwd.c 1.4.3.38"
/* Copyright (c) 1987, 1988 Microsoft Corporation */
/* All Rights Reserved */
/* This Module contains Proprietary Information of Microsoft */
/* Corporation and should be treated as Confidential. */
/*
* Usage:
*
* Administrative:
*
* passwd [name]
* passwd [-l|-d] [-n min] [-f] [-x max] [-w warn] name
* passwd -s [-a]
* passwd -s [name]
*
* Non-administrative:
*
* passwd [-s] [name]
*
* Level: SYS_PUBLIC
*
* Inheritable Privileges: P_AUDIT,P_DEV,P_MACWRITE,P_DACREAD,P_DACWRITE
* P_SYSOPS,P_SETFLEVEL,P_OWNER
*
* Fixed Privileges: P_MACREAD
*
* Files: /etc/passwd
* /etc/shadow
* /etc/.pwd.lock
* /etc/default/passwd
* /etc/security/ia/index
* /etc/security/ia/master
*
* Notes: passwd is a program whose sole purpose is to manage
* the password file. It allows system administrator
* to add, change and display password attributes.
* Non privileged user can change password or display
* password attributes which corresponds to their login name.
*/
/* LINTLIBRARY */
#include <stdio.h>
#include <signal.h>
#include <pwd.h>
#include <shadow.h>
#include <libgen.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <string.h>
#include <ctype.h> /* isalpha(c), isdigit(c), islower(c), toupper(c) */
#include <errno.h>
#include <crypt.h>
#include <deflt.h>
#include <limits.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <ia.h>
#include <sys/systeminfo.h>
#include <locale.h>
#include <pfmt.h>
#include <sat.h>
#include <sys/resource.h>
#include <sys/mac.h>
#ifdef _SHAREII
#include <dlfcn.h>
#include <shareIIhooks.h>
SH_DECLARE_HOOK(SETMYNAME);
SH_DECLARE_HOOK(ISUSERADMIN);
SH_DECLARE_HOOK(PWPERM);
#endif /* _SHAREII */
#define PWADMIN "passwd"
#define MAXSTRS 20 /* max num of strings from password generator */
#define MINWEEKS -1 /* minimum weeks before next password change */
#define MAXWEEKS -1 /* maximum weeks before password change */
#define MINLENGTH 6 /* minimum length for passwords */
#define WARNWEEKS -1 /* number of weeks before password expires
to warn the user */
#define PWHISTORY "/etc/passwd.history"
#define PWHISTTEMP "/etc/.passwd.history"
#ifdef DEBUG
#undef PASSWD
#define PASSWD "/usr/tmp/passwd"
#undef SHADOW
#define SHADOW "/usr/tmp/shadow"
#undef OPASSWD
#define OPASSWD "/usr/tmp/opasswd"
#undef OSHADOW
#define OSHADOW "/usr/tmp/oshadow"
#undef PASSTEMP
#define PASSTEMP "/usr/tmp/ptmp"
#undef SHADTEMP
#define SHADTEMP "/usr/tmp/stmp"
#undef PWHISTORY
#define PWHISTORY "/usr/tmp/passwd.history"
#undef PWHISTTEMP
#define PWHISTTEMP "/usr/tmp/.passwd.history"
#endif
/* flags indicate password attributes to be modified */
#define LFLAG 0x001 /* lock user's password */
#define DFLAG 0x002 /* delete user's password */
#define MFLAG 0x004 /* max field -- # of days passwd is valid */
#define NFLAG 0x008 /* min field -- # of days between passwd mods */
#define SFLAG 0x010 /* display password attributes */
#define FFLAG 0x020 /* expire user's password */
#define AFLAG 0x040 /* display password attributes for all users*/
#define PFLAG 0x080 /* change user's password */
#define WFLAG 0x100 /* warn user to change passwd */
#define PWGEN 0x077 /* passgen byte in flag field */
#define SAFLAG (SFLAG|AFLAG) /* display password attributes for all users*/
/* exit codes */
#define SUCCESS 0 /* succeeded */
#define NOPERM 1 /* No permission */
#define BADSYN 2 /* Incorrect syntax */
#define FMERR 3 /* File manipulation error */
#define FATAL 4 /* Old file can not be recover */
#define FBUSY 5 /* Lock file busy */
#define BADOPT 6 /* Invalid argument to option */
#define UFAIL 7 /* Unexpected failure */
#define NOID 8 /* Unknown ID */
#define BADAGE 9 /* Aging is disabled */
/* define error messages */
static const char
*sorry = ":353:Sorry\n",
*again = ":364:Try again later.\n",
/* usage message of non-password administrator */
*usage = ":362:Usage: passwd [-s] [name]\n",
*MSG_NP = ":354:Permission denied\n",
*MSG_NV = ":356:Invalid argument to option -%c\n",
*MSG_BS = ":357:Invalid combination of options\n",
*MSG_FE = ":815:Unexpected failure. Password file(s) unchanged.\n",
*MSG_FF = ":816:Unexpected failure. Password file(s) missing.\n",
*MSG_FB = ":360:Password file(s) busy.\n",
*MSG_UF = ":817:Unexpected failure.\n",
*MSG_UI = ":818:Unknown logname: %s\n",
*MSG_AD = ":361:Password aging is disabled\n",
*MSG_NO = ":819:Password may only be changed at login time\n",
*badusage = ":8:Incorrect usage\n",
*badentry = ":365:Bad entry found in the password file.\n",
*pwgenerr = ":820:Must select an offered value\n",
*pwgenmsg = ":821:Select a password from the following:\n\n",
/* usage message of password administrator */
*sausage = ":363:Usage:\n\tpasswd [name]\n\tpasswd [-l|-d] [-n min] [-f] [-x max] [-w warn] name\n\tpasswd -s [-a]\n\tpasswd -s [name]\n";
#define FAIL -1
#define NUMCP 13 /* number of characters for valid password */
/* print password status */
#define PRT_PWD(pwdp) {\
if (*pwdp == NULL) \
(void) fprintf(stdout, "NP ");\
else if (strlen(pwdp) < (size_t) NUMCP) \
(void) fprintf(stdout, "LK ");\
else (void) fprintf(stdout, "PS ");\
}
#define PRT_AGE() {\
if (mastp->ia_max != -1) { \
if (mastp->ia_lstchg) {\
lstchg = mastp->ia_lstchg * DAY;\
tmp = gmtime(&lstchg);\
(void) fprintf(stdout,"%.2d/%.2d/%.2d ",(tmp->tm_mon + 1),tmp->tm_mday,tmp->tm_year%100);\
} else (void) fprintf(stdout,"00/00/00 ");\
if ((mastp->ia_min >= 0) && (mastp->ia_warn > 0))\
(void) fprintf(stdout, "%ld %ld %ld ", mastp->ia_min, mastp->ia_max, mastp->ia_warn);\
else if (mastp->ia_min >= 0) \
(void) fprintf(stdout, "%ld %ld ", mastp->ia_min, mastp->ia_max);\
else if (mastp->ia_warn > 0) \
(void) fprintf(stdout, " %ld %ld ", mastp->ia_max, mastp->ia_warn);\
else \
(void) fprintf(stdout, " %ld ", mastp->ia_max);\
}\
}
/* Macros used for password aging when not using shadow passwords */
#define DAY_WEEK 7 /* days per weeks */
#define DIG_CH 63 /* A character represents upto 64
digits in a radix-64 notation */
#define SEC_WEEK (24 * DAY_WEEK * 60 * 60 ) /* seconds per week */
/* Convert base-64 ASCII string to long integer. *
* Convert long integer to maxweek, minweeks. *
* Convert current time from seconds to weeks. *
*/
#define CNV_AGE() {\
when = (long) a64l(pwd->pw_age);\
maxweeks = when & 077;\
minweeks = (when >> 6) & 077;\
now = time ((long *) 0) / SEC_WEEK;\
}
extern int auditctl(),
putiaent();
extern int _getpwent_no_yp; /* no YP interp of /etc/passwd ents */
extern int _getpwent_no_shadow; /* no passwds from /etc/shadow */
/* SGI IRIX 6.2 getpwent merges in SSDI, which allows custom implementations
* of passwd database (and others as well). With SSDI, getpwent will
* return entries from these custom implemetations, whereas passmt
* only needs stuff from the "files". Setting _getpwent_no_ssdi to 1
* turns off this behavior which is what passwd needs.
*/
extern int _getpwent_no_ssdi;
static struct master *mastp;
static char *pw;
static int circ(),
ckarg(),
update(),
display(),
pwd_adm(),
ck_ifadm(),
ck_passwd(),
std_pwgen(),
update_sfile(),
update_noshadow();
static int retval = 0,
login_only = 0,
mindate, maxdate, /* password aging information */
historycnt, historydays,
warndate, minlength;
static void call_pwgen(),
init_defaults();
static FILE *get_pwgen(),
*open_pwgen(),
*get_pwvalidate(),
*open_pwval();
static char nullstr[] = "";
static char newcryptpw[PASS_MAX+1];
/*
* Procedure: main
*
* Restrictions:
* getpwuid: none
* getiasz: none
* getianam: none
* lckpwdf: none
* ulckpwdf: none
*/
main(argc, argv)
int argc;
char **argv;
{
int flag,
pw_admin;
uid_t uid;
char *uname;
struct rlimit rlimits;
rlimits.rlim_cur=0;
rlimits.rlim_max=0;
setrlimit( RLIMIT_CORE, &rlimits);
(void)setlocale(LC_ALL, "");
(void)setcat("uxcore.abi");
(void)setlabel("UX:passwd");
/*
* Disable YP interpretation of /etc/passwd. This is necessary
* so we can make an accurate copy via putpwent. For the same
* reason, disable shadowing of /etc/shadow passwords.
*/
_getpwent_no_yp = 1;
_getpwent_no_shadow = 1;
/* SGI: turn off SSDI mapping for getpwent */
_getpwent_no_ssdi = 1;
/*
* get the real user ID. The effective user ID will always
* be 0 since "passwd" is a setuid-on-exec to 0 program.
*/
uid = getuid();
/*
* call the init_defaults() routine to set the tunables
* used by "passwd".
*/
init_defaults();
/*
* a return value > 0 from ck_ifadm indicates that this
* is being run by a password administrator. Obviously,
* 0 means non-administrator.
*/
pw_admin = ck_ifadm(uid);
#ifdef _SHAREII
/*
* If we already have admin status, no ShareII checks
* are needed. But if we don't, we may still have
* permission if we have a ShareII Admin or Uselim flag.
*
* A Uselim flag gives us all the privileges of a normal
* password administrator.
* An Admin flag gives us privileges only over those
* accounts within our scheduling group. Further checks
* will need to be made later when we know the user
* whose password is being changed.
*/
if (!pw_admin && sgidladd(SH_LIMITS_LIB, RTLD_LAZY))
{
static const char *Myname = "passwd";
SH_HOOK_SETMYNAME(Myname);
pw_admin = SH_HOOK_ISUSERADMIN();
}
#endif /* _SHAREII */
/*
* ckarg() parses the arguments. In case of an error it
* sets the retval and returns FAIL (-1). It returns the
* value indicating which password attributes to modify.
*/
switch (flag = ckarg(argc, argv, pw_admin, uid)) {
case FAIL: /* failed */
exit(retval);
/* NOTREACHED */
case SAFLAG: /* display password attributes */
exit(display((struct master *) NULL));
/* NOTREACHED */
default:
break;
}
argc -= optind;
if (argc < 1 ) {
struct passwd *p_ptr;
/* We enable YP interpretation around this call
since the caller might be registered in YP. */
_getpwent_no_yp = 0;
if ((p_ptr = getpwuid(uid)) == NULL) {
(void)pfmt(stderr, MM_ERROR, badusage);
(void)pfmt(stderr, MM_ACTION, !pw_admin ? usage : sausage);
exit(NOPERM);
}
_getpwent_no_yp = 1;
uname = strdup(p_ptr->pw_name);
/*
* if flag set, must be displaying or modifying
* password aging attributes
*/
if (!flag)
(void) pfmt(stderr, MM_INFO,
":366:Changing password for %s\n", uname);
}
else
uname = argv[optind];
if (ia_openinfo(uname, &mastp) < 0) {
char *yp_uname;
/* ia_openinfo may have failed because the user is
an entry prefixed by '+' (i.e., YP user).
So we perform the operation again. */
yp_uname = malloc(strlen(uname) + 2);
yp_uname[0] = '+';
strcpy (&yp_uname[1], uname);
uname = yp_uname;
/* turn NIS back on to get the info, turn it off afterwards
* Note that NIS wants the name without the + that we have added
* for use in the passwd file itself
*/
_getpwent_no_yp = 0;
if (ia_openinfo(&uname[1], &mastp) < 0) {
(void) pfmt(stderr, MM_ERROR, MSG_UF);
exit(UFAIL);
}
_getpwent_no_yp = 1;
}
/*
* the following statement determines who is allowed to
* modify a password. If this user isn't a password
* administrator and the real uid isn't equal to the
* user's password that was requested or UID_MAX, then
* the user doesn't have permission to change the password.
*
* NOTE: a real uid of UID_MAX indicates that this
* was called from the login scheme.
*/
#ifdef _SHAREII
/*
* The pw_admin flag may be set because of SHAREII.
* If this is the only authorisation we have, we need
* to do a further check now that we know the user that
* is having their password changed.
*/
if (!ck_ifadm(uid) && uid != UID_MAX) {
#else /* _SHAREII */
if (!pw_admin && uid != UID_MAX) {
#endif /* _SHAREII */
struct master *cmp_mastp = mastp;
/*
* If this is a YP entry, then we need to get
* the UID by querying YP. We do this by
* calling ia_openinfo again with YP interpretation.
*/
if (uname[0] == '+')
{
_getpwent_no_yp = 0;
/*
* When you turn on YP interpretation, the
* name must not begin with '+'.
*/
if (ia_openinfo(&uname[1], &cmp_mastp) < 0) {
(void) pfmt(stderr, MM_ERROR, MSG_UF);
exit(UFAIL);
}
_getpwent_no_yp = 1;
}
#ifdef _SHAREII
/*
* We get here if we don't have standard (non-ShareII)
* passwd admin privileges.
* We may be either an ordinary non-admin user (pw_admin == 0)
* or a user with the ShareII Admin or Uselim flag set
* (pw_admin == 1).
* If ordinary user, we just test to make sure the password
* we are changing is our own.
* If ShareII Admin or Uselim, we use SHpwperm to see if
* we have permission on the password we are changing.
*/
if (
uid != cmp_mastp->ia_uid
&&
(
!pw_admin
||
!SH_HOOK_PWPERM(uid, cmp_mastp->ia_uid)
)
)
{
#else /* _SHAREII */
if (uid != cmp_mastp->ia_uid) {
#endif /* _SHAREII */
(void) pfmt(stderr, MM_ERROR, MSG_NP);
exit(NOPERM);
}
}
/*
* If the flag is set display/update password attributes.
*/
switch (flag) {
case SFLAG: /* display password attributes */
exit(display(mastp));
/* NOTREACHED */
case 0: /* changing user password */
break;
default: /* changing user password attributes */
/* lock the password file */
if (lckpwdf() != 0) {
(void) pfmt(stderr, MM_ERROR, MSG_FB);
(void) pfmt(stderr, MM_ACTION, again);
exit(FBUSY);
}
retval = update(flag, uname);
(void) ulckpwdf();
exit(retval);
}
/*
* prompt for the users new password, check the triviality
* and verify the password entered is correct.
*/
if ((retval = ck_passwd(&flag, pw_admin, uname)) != SUCCESS) {
exit(retval);
}
/* lock the password file */
if (lckpwdf() != 0) {
(void) pfmt(stderr, MM_ERROR, MSG_FB);
(void) pfmt(stderr, MM_ACTION, again);
exit(FBUSY);
}
flag |= PFLAG;
retval = update(flag, uname);
(void) ulckpwdf();
if (retval == SUCCESS) {
cap_value_t cap_audit_write = CAP_AUDIT_WRITE;
cap_t ocap = cap_acquire(1, &cap_audit_write);
ia_audit("PASSWD", uname, 1, "Updated password/shadow files");
cap_surrender(ocap);
}
exit(retval);
/* NOTREACHED */
}
/*
* Procedure: ck_passwd
*
* Restrictions:
* getpass: none
*
* Notes: Verify users old password. It also checks password
* aging information to verify that user is authorized
* to change password. Also, prompt for the users new
* password, check the triviality and verify that the
* password entered is correct.
*/
static int
ck_passwd(flagp, adm, uname)
int *flagp,
adm;
char *uname;
{
/*
* passwd calls getpass() to get the password. The getpass() routine
* returns at most PASS_MAX charaters.
*/
register int now,
err = 0;
char *pswd,
buf[PASS_MAX+1],
saltc[2], /* crypt() takes 2 char string as salt */
pwbuf[PASS_MAX+1],
opwbuf[PASS_MAX+1];
time_t salt;
int ret, i, c, /* for triviality checks */
pw_genchar = 0;
FILE *iop;
struct stat statbuf;
cap_t ocap;
cap_value_t cap_audit_write = CAP_AUDIT_WRITE;
ret = SUCCESS;
pwbuf[0] = buf[0] = '\0'; /* used for "while" loop */
/*
* if the user isn't a password administrator and they had an
* old password, get it and verify.
*/
if (!adm && *mastp->ia_pwdp) {
if ((pswd = getpass(gettxt(":378", "Old password:"))) == NULL) {
ocap = cap_acquire(1, &cap_audit_write);
ia_audit("PASSWD", uname, 0,
"Didn't provide old password");
cap_surrender(ocap);
(void) pfmt(stderr, MM_ERROR, sorry);
return FMERR;
}
else {
(void) strcpy(opwbuf, pswd);
pw = crypt(opwbuf, mastp->ia_pwdp);
}
if (strcmp(pw, mastp->ia_pwdp) != 0) {
ocap = cap_acquire(1, &cap_audit_write);
ia_audit("PASSWD", uname, 0,
"Didn't provide correct old password");
cap_surrender(ocap);
(void) pfmt(stderr, MM_ERROR, sorry);
return NOPERM;
}
}
else {
opwbuf[0] = '\0';
}
/* password age checking applies */
if (mastp->ia_max != -1 && mastp->ia_lstchg != 0) {
now = DAY_NOW;
if (mastp->ia_lstchg <= now) {
if (!adm && ( now < mastp->ia_lstchg + mastp->ia_min)) {
ocap = cap_acquire(1, &cap_audit_write);
ia_audit("PASSWD", uname, 0,
"Changed too recently");
cap_surrender(ocap);
(void) pfmt(stderr, MM_ERROR,
":379:Sorry: < %ld days since the last change\n",
mastp->ia_min);
return NOPERM;
}
if (mastp->ia_min > mastp->ia_max && !adm) {
ocap = cap_acquire(1, &cap_audit_write);
ia_audit("PASSWD", uname, 0,
"password not changeable by user");
cap_surrender(ocap);
(void) pfmt(stderr, MM_ERROR,
":380:You may not change this password\n");
return NOPERM;
}
}
}
/* aging is turned on */
else if(mastp->ia_lstchg == 0 && mastp->ia_max > 0 || mastp->ia_min > 0) {
ret = SUCCESS;
}
else if (stat(SHADOW, &statbuf) == 0) {
/* aging not turned on */
/* so turn on passwd for user with default values */
*flagp |= MFLAG;
*flagp |= NFLAG;
*flagp |= WFLAG;
}
/*
* if this system supports password generating programs,
* "exec" the program now. If the generator fails, or the
* the expected password generator does not exist, follow
* the standard password algorithm.
*/
/* the PWGEN flag is never set in IRIX, and we ignore pw_genchar */
if (!adm && /*(pw_genchar = (mastp->ia_flag & PWGEN)) &&*/
((iop = get_pwgen(pw_genchar)) != NULL)) {
call_pwgen(iop, (char *)&buf, (char *)&pwbuf);
}
/*
* didn't call password generator or it failed for some
* reason so now get the password the old-fashioned way.
*/
if (!strlen(buf)) {
if ((err = std_pwgen(adm, uname, opwbuf,
(char *)&buf, (char *)&pwbuf))) {
return err;
}
}
/* if this system has been configured for an external password
* validator, call it with the new password.
*/
if (!adm && ((iop = get_pwvalidate()) != NULL))
{
if ( (err = call_pwvalidate(iop,pwbuf)) != 0 )
{
fprintf(stderr,"Sorry, your new password is not acceptable\n");
fflush(stderr);
return err;
}
}
/*
* Construct salt, then encrypt the new password
*/
(void) time((time_t *)&salt);
salt += (long)getpid();
saltc[0] = salt & 077;
saltc[1] = (salt >> 6) & 077;
for (i = 0; i < 2; i++) {
c = saltc[i] + '.';
if (c>'9') c += 7;
if (c>'Z') c += 6;
saltc[i] = (char) c;
}
pw = crypt(pwbuf, saltc);
strcpy(newcryptpw,pw);
pw = &newcryptpw[0]; /* crypt has a static buffer and we'll be
* using crypt in ck_pwhistory, so save the
* new encrypted passwd
*/
if ( !adm && ((err = ck_pwhistory(uname,pwbuf)) != SUCCESS ) )
return err;
return ret;
}
/*
* Procedure: update
*
* Restrictions:
* stat() none
* unlink() none
* creat() none
* fopen() none
* getspent() none
* putspent() none
* chmod() none
* chown() none
*
* Notes: updates the password file. It takes "flag" as an argument
* to determine which password attributes to modify. It returns
* 0 for success and > 0 for failure.
*
* Side effect: Variable sp points to NULL.
*/
static int
update(flag, uname)
int flag;
char *uname;
{
register int i, found = 0;
struct stat buf;
struct spwd *sp;
int fd,
sp_error = 0,
end_of_file = 0;
FILE *tsfp;
mac_t new_lp, orig_lp = (mac_t) 0;
int retval, mac_enabled = 0;
/*
If mac enabled, save the current label
*/
if ((mac_enabled = (sysconf(_SC_MAC)) > 0)) {
if ((orig_lp = mac_get_proc()) == (mac_t) NULL ) {
(void) pfmt(stderr, MM_ERROR, MSG_UF);
exit(UFAIL);
}
}
/* ignore all the signals */
for (i = 1; i < NSIG; i++)
(void) sigset(i, SIG_IGN);
/* Clear the errno. It is set because SIGKILL can not be ignored. */
errno = 0;
/*
* Mode of the shadow file should be 400.
* We should be able to read it for two reasons:
*
* 1) the process has the P_MACREAD privilege.
*
* 2) the discretionary access has been set to
* "root", owner of the shadow file.
*/
if (stat(SHADOW, &buf) < 0) {
/*
If mac is enabled, retrieve the mac from the passwd
file and set the process to that label.
*/
if (mac_enabled) {
if ((new_lp = mac_get_file (PASSWD)) == (mac_t) NULL ) {
(void) pfmt(stderr, MM_ERROR, MSG_UF);
exit(UFAIL);
}
if (mac_set_proc (new_lp) == -1) {
(void) pfmt(stderr, MM_ERROR, MSG_UF);
exit(UFAIL);
}
(void) mac_free(new_lp);
}
update_noshadow(flag, uname);
if (mac_enabled) {
(void) mac_set_proc (orig_lp);
(void) mac_free(orig_lp);
}
update_pwhistory(uname,pw);
return 0;
/*
* (void) pfmt(stderr, MM_ERROR, MSG_FE);
* return FMERR;
*/
}
/*
If mac is enabled, retrieve the mac from the shadow
file and set the process to that label.
*/
if (mac_enabled) {
if ((new_lp = mac_get_file (SHADOW)) == (mac_t) NULL ) {
(void) pfmt(stderr, MM_ERROR, MSG_UF);
exit(UFAIL);
}
if (mac_set_proc (new_lp) == -1) {
(void) pfmt(stderr, MM_ERROR, MSG_UF);
exit(UFAIL);
}
(void) mac_free(new_lp);
}
/* SGI: lose the leading + when updating shadow file */
if ( *uname == '+' ) uname++;
/*
* do unconditional unlink of temporary shadow file.
* Shouldn't exist anyway!!
*/
(void) unlink(SHADTEMP);
/*
* create temporary file.
*/
if ((fd = creat(SHADTEMP, 0600)) == FAIL) {
/*
* couldn't create temporary shadow file.
* Big problems, better exit with message.
*/
if (mac_enabled) {
(void) mac_set_proc (orig_lp);
(void) mac_free(orig_lp);
}
(void) pfmt(stderr, MM_ERROR, MSG_FE);
return FMERR;
}
(void) close(fd);
if ((tsfp = fopen(SHADTEMP, "w")) == NULL) {
if (mac_enabled) {
(void) mac_set_proc (orig_lp);
(void) mac_free(orig_lp);
}
(void) pfmt(stderr, MM_ERROR, MSG_FE);
return FMERR;
}
/*
* copy passwd file to temp, replacing matching lines
* with new password attributes.
*/
end_of_file = sp_error = 0;
/*
* while it would seem to be good security practice to turn
* OFF the P_MACREAD privilege at this point, it isn't because
* "passwd" has that privilege in it's FIXED set to be able to
* READ this file. Therefore, turning it OFF would be an ERROR.
*/
while (!end_of_file) {
if ((sp = getspent()) != NULL) {
if (strcmp(sp->sp_namp, uname) == 0) {
found = 1;
/* LFLAG and DFLAG should be checked before FFLAG.
FFLAG clears the sp_lstchg field. We do not
want sp_lstchg field to be set if one execute
passwd -d -f name or passwd -l -f name.
*/
if (flag & LFLAG) { /* lock password */
sp->sp_pwdp = pw;
sp->sp_lstchg = DAY_NOW;
(void) strcpy(mastp->ia_pwdp, pw);
mastp->ia_lstchg = DAY_NOW;
}
if (flag & DFLAG) { /* delete password */
/*
* set the aging information to -1.
*/
sp->sp_min = -1;
sp->sp_max = -1;
mastp->ia_min = -1;
mastp->ia_max = -1;
/*
* NULL out the current password
*/
sp->sp_pwdp = nullstr;
sp->sp_lstchg = DAY_NOW;
mastp->ia_pwdp[0] = '\0';
mastp->ia_lstchg = DAY_NOW;
}
if (flag & FFLAG) { /* expire password */
sp->sp_lstchg = (long) 0;
mastp->ia_lstchg = (long) 0;
}
if (flag & MFLAG) { /* set max field */
if (!(flag & NFLAG) && mastp->ia_min == -1) {
sp->sp_min = 0;
mastp->ia_min = 0;
}
if (maxdate == -1) { /* trun off aging */
sp->sp_min = -1;
mastp->ia_min = -1;
sp->sp_warn = -1;
mastp->ia_warn = -1;
}
else if (mastp->ia_max == -1) {
sp->sp_lstchg = 0;
mastp->ia_lstchg = 0;
}
sp->sp_max = maxdate;
mastp->ia_max = maxdate;
}
if (flag & NFLAG) { /* set min field */
if (mastp->ia_max == -1 && mindate != -1) {
(void) pfmt(stderr, MM_ERROR, MSG_AD);
(void) pfmt(stderr, MM_ACTION, sausage);
endspent();
(void) unlink(SHADTEMP);
if (mac_enabled) {
(void) mac_set_proc (orig_lp);
(void) mac_free(orig_lp);
}
return BADAGE;
}
sp->sp_min = mindate;
mastp->ia_min = mindate;
}
if (flag & WFLAG) { /* set warn field */
if (mastp->ia_max == -1 && warndate != -1) {
(void) pfmt(stderr, MM_ERROR, MSG_AD);
(void) pfmt(stderr, MM_ACTION, sausage);
endspent();
(void) unlink(SHADTEMP);
if (mac_enabled) {
(void) mac_set_proc (orig_lp);
(void) mac_free(orig_lp);
}
return BADAGE;
}
sp->sp_warn = warndate;
mastp->ia_warn = warndate;
}
if (flag & PFLAG) { /* change password */
sp->sp_pwdp = pw;
(void) strcpy(mastp->ia_pwdp, pw);
/* update the last change field */
sp->sp_lstchg = DAY_NOW;
mastp->ia_lstchg = DAY_NOW;
if (mastp->ia_max == 0) { /* turn off aging */
sp->sp_max = -1;
sp->sp_min = -1;
mastp->ia_max = -1;
mastp->ia_min = -1;
}
}
}
if (putspent (sp, tsfp) != 0) {
endspent();
(void) unlink(SHADTEMP);
if (mac_enabled) {
(void) mac_set_proc (orig_lp);
(void) mac_free(orig_lp);
}
(void) pfmt(stderr, MM_ERROR, MSG_FE);
return FMERR;
}
} else {
if (errno == 0)
/* end of file */
end_of_file = 1;
else if (errno == EINVAL) {
/*bad entry found in /etc/shadow, skip it */
errno = 0;
sp_error++;
} else
/* unexpected error found */
end_of_file = 1;
}
} /*end of while*/
endspent();
if (sp_error >= 1)
pfmt(stderr, MM_ERROR, badentry);
if (fclose (tsfp)) {
(void) unlink(SHADTEMP);
if (mac_enabled) {
(void) mac_set_proc (orig_lp);
(void) mac_free(orig_lp);
}
(void) pfmt(stderr, MM_ERROR, MSG_FE);
return FMERR;
}
/*
* Check if user name exists
*/
if (!found) {
(void) unlink(SHADTEMP);
if (mac_enabled) {
(void) mac_set_proc (orig_lp);
(void) mac_free(orig_lp);
}
(void) pfmt(stderr, MM_ERROR, MSG_FE);
return FMERR;
}
/* we're committed at this pt, so go ahead and update the history file */
update_pwhistory(uname,pw);
/*
* change the mode of the temporary file to the mode
* of the original shadow file.
*/
if (chmod(SHADTEMP, buf.st_mode) != SUCCESS) {
(void) unlink(SHADTEMP);
if (mac_enabled) {
(void) mac_set_proc (orig_lp);
(void) mac_free(orig_lp);
}
(void) pfmt(stderr, MM_ERROR, MSG_FE);
return FMERR;
}
/*
* Do a chown() on the temporary shadow file to the
* owner and group of the original shadow file.
*/
if (chown(SHADTEMP, buf.st_uid, buf.st_gid) != SUCCESS) {
(void) unlink(SHADTEMP);
if (mac_enabled) {
(void) mac_set_proc (orig_lp);
(void) mac_free(orig_lp);
}
(void) pfmt(stderr, MM_ERROR, MSG_FE);
return FMERR;
}
/*
* Rename temp file back to appropriate passwd file and return.
*/
retval = update_sfile(SHADOW, OSHADOW, SHADTEMP, uname);
if (mac_enabled) {
(void) mac_set_proc (orig_lp);
(void) mac_free(orig_lp);
}
return retval;
}
/*
* Procedure: circ
*
* Notes: compares the password entered by user with the login name
* of the user to determine if the password is just a circular
* representation of the users login name. Since this is a
* common practice for ease of remembering one's password, it
* is disallowed for just that reason; someone attempting to
* "break-in" would use this method and therefore be successful.
*/
static int
circ(s, t)
char *s, *t;
{
char c, *p, *o, *r, buff[25], ubuff[25], pubuff[25];
int i, j, k, l, m;
m = 2;
i = strlen(s);
o = &ubuff[0];
for (p = s; c = *p++; *o++ = c)
if (islower(c))
c = toupper(c);
*o = '\0';
o = &pubuff[0];
for (p = t; c = *p++; *o++ = c)
if (islower(c))
c = toupper(c);
*o = '\0';
p = &ubuff[0];
while (m--) {
for (k = 0; k <= i; k++) {
c = *p++;
o = p;
l = i;
r = &buff[0];
while (--l)
*r++ = *o++;
*r++ = c;
*r = '\0';
p = &buff[0];
if (strcmp(p, pubuff) == 0)
return 1;
}
p = p + i;
r = &ubuff[0];;
j = i;
while (j--)
*--p = *r++;
}
return SUCCESS;
}
/*
* Procedure: ckarg
*
* Restrictions:
* access(2): none
*
* Notes: This function parses and verifies the
* arguments. It takes three parameters:
*
* argc => # of arguments
* argv => pointer to an argument
* adm => password administrator flag
*
* In case of an error it prints the appropriate error
* message, sets the retval and returns FAIL(-1).
*/
static int
ckarg(argc, argv, adm, real_uid)
int argc;
char **argv;
int adm;
uid_t real_uid;
{
extern char *optarg;
register int c, flag = 0;
char *char_p,
*lkstring = "*LK*"; /*lock string to lock user's password*/
while ((c = getopt(argc, argv, "aldfsx:n:w:")) != EOF) {
switch (c) {
case 'd': /* delete the password */
/* Only password administrator can execute this */
if (!pwd_adm(adm))
return FAIL;
if (flag & (LFLAG|SAFLAG|DFLAG)) {
(void) pfmt(stderr,MM_ERROR, MSG_BS);
(void) pfmt(stderr, MM_ACTION, sausage);
retval = BADSYN;
return FAIL;
}
flag |= DFLAG;
pw = '\0';
break;
case 'l': /* lock the password */
/* Only password administrator can execute this */
if (!pwd_adm(adm))
return FAIL;
if (flag & (DFLAG|SAFLAG|LFLAG)) {
(void) pfmt(stderr,MM_ERROR, MSG_BS);
(void) pfmt(stderr, MM_ACTION, sausage);
retval = BADSYN;
return FAIL;
}
flag |= LFLAG;
pw = lkstring;
break;
case 'x': /* set the max date */
/* Only password administrator can execute this */
if (!pwd_adm(adm))
return FAIL;
if (flag & (SAFLAG|MFLAG)) {
(void) pfmt(stderr,MM_ERROR, MSG_BS);
(void) pfmt(stderr, MM_ACTION, sausage);
retval = BADSYN;
return FAIL;
}
flag |= MFLAG;
if ((maxdate = (int) strtol(optarg, &char_p, 10)) < -1
|| *char_p != '\0' || strlen(optarg) <= (size_t) 0) {
(void) pfmt(stderr, MM_ERROR, MSG_NV, 'x');
retval = BADOPT;
return FAIL;
}
break;
case 'n': /* set the min date */
/* Only password administrator can execute this */
if (!pwd_adm(adm))
return FAIL;
if (flag & (SAFLAG|NFLAG)) {
(void) pfmt(stderr,MM_ERROR, MSG_BS);
(void) pfmt(stderr, MM_ACTION, sausage);
retval = BADSYN;
return FAIL;
}
flag |= NFLAG;
if (((mindate = (int) strtol(optarg, &char_p,10)) < 0
|| *char_p != '\0') || strlen(optarg) <= (size_t)0) {
(void) pfmt(stderr, MM_ERROR, MSG_NV, 'n');
retval = BADOPT;
return FAIL;
}
break;
case 'w': /* set the warning field */
/* Only password administrator can execute this */
if (!pwd_adm(adm))
return FAIL;
if (flag & (SAFLAG|WFLAG)) {
(void) pfmt(stderr,MM_ERROR, MSG_BS);
(void) pfmt(stderr, MM_ACTION, sausage);
retval = BADSYN;
return FAIL;
}
flag |= WFLAG;
if (((warndate = (int) strtol(optarg, &char_p,10)) < 0
|| *char_p != '\0') || strlen(optarg) <= (size_t)0) {
(void) pfmt(stderr, MM_ERROR, MSG_NV, 'w');
retval = BADOPT;
return FAIL;
}
break;
case 's': /* display password attributes */
if (flag && (flag != AFLAG)) {
(void) pfmt(stderr,MM_ERROR, MSG_BS);
(void) pfmt(stderr, MM_ACTION, !adm ? usage : sausage);
retval = BADSYN;
return FAIL;
}
flag |= SFLAG;
break;
case 'a': /* display password attributes */
/* Only password administrator can execute this */
if (!pwd_adm(adm))
return FAIL;
if (flag && (flag != SFLAG)) {
(void) pfmt(stderr,MM_ERROR, MSG_BS);
(void) pfmt(stderr, MM_ACTION, sausage);
retval = BADSYN;
return FAIL;
}
flag |= AFLAG;
break;
case 'f': /* expire password attributes */
/* Only password administrator can execute this */
if (!pwd_adm(adm))
return FAIL;
if (flag & (SAFLAG|FFLAG)) {
(void) pfmt(stderr,MM_ERROR, MSG_BS);
(void) pfmt(stderr, MM_ACTION, sausage);
retval = BADSYN;
return FAIL;
}
flag |= FFLAG;
break;
case '?':
(void) pfmt(stderr, MM_ACTION, !adm ? usage : sausage);
retval = BADSYN;
return FAIL;
}
}
argc -= optind;
if (argc > 1) {
(void) pfmt(stderr, MM_ERROR, badusage);
(void) pfmt(stderr, MM_ACTION, !adm ? usage : sausage);
retval = BADSYN;
return FAIL;
}
/*
* if no options specified, or only the show option, return.
*/
if (!flag || (flag == SFLAG)) {
return flag;
}
if (flag == AFLAG) {
(void) pfmt(stderr, MM_ERROR, badusage);
(void) pfmt(stderr, MM_ACTION, sausage);
retval = BADSYN;
return FAIL;
}
if (flag != SAFLAG && argc < 1) {
(void) pfmt(stderr, MM_ERROR, badusage);
(void) pfmt(stderr, MM_ACTION, sausage);
retval = BADSYN;
return FAIL;
}
if (flag == SAFLAG && argc >= 1) {
(void) pfmt(stderr, MM_ERROR, badusage);
(void) pfmt(stderr, MM_ACTION, sausage);
retval = BADSYN;
return FAIL;
}
if ((maxdate == -1) && (flag & NFLAG)) {
(void) pfmt(stderr, MM_ERROR, MSG_NV, 'x');
retval = BADOPT;
return FAIL;
}
return flag;
}
/*
* Procedure: display
*
* Restrictions:
* open() none
* getianam() none
* stat(2): none
* mmap(2): none
*
* Notes: displays password attributes. It takes master struct
* pointer as a parameter. If the pointer is NULL then it
* displays password attributes for all entries on the file.
* Returns 0 for success and positive integer for failure.
*/
static int
display(mastp)
struct master *mastp;
{
void display_one_entry();
int found = 0;
if (mastp != NULL) {
display_one_entry(mastp);
return SUCCESS;
}
errno = 0;
while (ia_openinfo_next(&mastp) == 0) {
found++;
display_one_entry(mastp);
}
if (found == 0) {
(void) pfmt(stderr, MM_ERROR, MSG_FF);
return (FATAL);
}
return (SUCCESS);
}
void display_one_entry(mastp)
struct master *mastp;
{
struct tm *tmp;
long lstchg;
char *name;
int ypflag = 0;
name = mastp->ia_name;
ypflag = 0;
if (name && *name == '+') { /* NIS entry */
name++;
ypflag++;
if (*name == '@') {
ypflag++;
name++;
}
}
if (ypflag == 1 && name[0] == '\0')
name = "Entire NIS map";
(void) fprintf(stdout,"%-15s ", name);
if (!ypflag || *mastp->ia_pwdp != NULL) {
PRT_PWD(mastp->ia_pwdp);
} else
(void) fprintf(stdout, " ");
switch (ypflag) {
default:
case 0:
(void) fprintf(stdout, "%5d %5d ", mastp->ia_uid, mastp->ia_gid);
break;
case 1:
(void) fprintf(stdout, "%-12s ", "NIS entry");
break;
case 2:
(void) fprintf(stdout, "%-12s ", "NIS netgroup");
break;
}
if (*mastp->ia_dirp != NULL)
(void) fprintf(stdout,"%s ", mastp->ia_dirp);
if (*mastp->ia_shellp != NULL)
(void) fprintf(stdout,"%s ", mastp->ia_shellp);
PRT_AGE();
(void) fprintf(stdout, "\n");
ia_closeinfo(mastp);
}
/*
* Procedure: pwd_adm
*
* Notes: determines if the user is a password administrator
* or not. If not, print a diagnostic message, set the
* return value for the process and return to the caller
* indicating the user is NOT an administrator. Other-
* wise return to the caller indicating the user is a
* password administrator.
*/
static int
pwd_adm(adm)
register int adm;
{
if(!adm) {
(void) pfmt(stderr, MM_ERROR, MSG_NP);
retval = NOPERM;
return 0;
}
return 1;
}
/*
* Procedure: ck_ifadm
*
* Restrictions:
* secsys(2): none
* sysinfo(2): SEE (2)
*
* Notes: "passwd" is an interesting command. It really has a
* split personality meaning that it must work correctly
* under different conditions. These are some of those
* conditions:
*
* 1) if the privilege mechanism is ID-based
* and the REAL user is privileged, then
* that user can also be the password admin-
* istrator.
*
* 2) if the privilege mechanism is file based
* (i.e., LPM), then the determination of
* whether or not the user is also the pass-
* word administrator is based on the premiss
* that if this process can adjust the system
* clock then the user is a password adminis-
* trator.
*
* Otherwise, the user is a non-administrator
* and limited to non-administrator type functions.
*
* SEE NOTE: below
*
* Further restrictions of what non-administrators can and
* can't do can be found in the "ckarg" routine.
*/
static int
ck_ifadm(user_uid)
uid_t user_uid;
{
return (0==user_uid);
}
/*
* Procedure: update_sfile
*
* Restrictions:
* access() none
* unlink() none
* rename() none
* putiaent() none
*
* Notes: This routine performs all the necessary checks when moving
* the current shadow file to the old shadow file and renaming
* the temporary shadow file to the current shadow file.
*/
static int
update_sfile(sfilep, osfilep, tsfilep, uname)
char *sfilep; /* shadow file */
char *osfilep; /* old shadow file */
char *tsfilep; /* temporary shadow file */
char *uname;
{
/*
* First check to see if there was an existing shadow file.
*/
if (access(sfilep, 0) == 0) {
/*
* if so, remove old shadow file
*/
if (access(osfilep, 0) == 0) {
if (unlink(osfilep) < 0) {
(void) unlink(tsfilep);
(void) pfmt(stderr, MM_ERROR, MSG_FE);
return FMERR;
}
}
/*
* link shadow file to old shadow file
*/
if (link(sfilep, osfilep) < 0 ) {
(void) unlink(tsfilep);
(void) pfmt(stderr, MM_ERROR, MSG_FE);
return FMERR;
}
}
/*
* rename temporary shadow file to shadow file
*/
if (rename(tsfilep, sfilep) < 0 ) {
(void) unlink(sfilep);
if (link(osfilep, sfilep) < 0 ) {
(void) pfmt(stderr, MM_ERROR, MSG_FF);
return FATAL;
}
(void) pfmt(stderr, MM_ERROR, MSG_FE);
return FMERR;
}
/* let NSD know about the change */
rmdir("/ns/.local/shadow.byname");
return SUCCESS;
}
/*
* Procedure: get_pwgen
*
* Restrictions:
* defopen: none
*
* Notes: does validation for determining what the password
* generator is, reads the default passwd file, and
* "exec" the password generator if it exists.
* This routine calls "open_pwgen" that creates a pipe and
* does a "fork()" and "exec()".
*/
static FILE *
get_pwgen(pw_ident)
int pw_ident;
{
char prog[BUFSIZ],
passgen[BUFSIZ],
*progp = &prog[0],
*pstrp = &passgen[0];
FILE *iop;
/*
* there is an inclination to clear the P_MACREAD privilege in
* this routine so that a user who doesn't have non-privileged
* access to the necessary files would fail, but that would be
* an error since ordinary users on a system with MAC installed
* but no file-based privilege mechanism also need access to
* these particular files.
*/
progp = '\0';
/* base code used multiple PASSGEN options, but the necessary
* flag was never being set, so just ignore it
*/
/*(void) sprintf(pstrp, "PASSGEN%c", pw_ident);*/
strcpy(pstrp,"PASSGEN");
if ((iop = defopen(PWADMIN)) == NULL) {
/*
* couldn't open "passwd" default file, no generator!
*/
return (FILE *)NULL;
}
if ((progp = defread(iop, pstrp)) == NULL) {
/*
* didn't find the password generator keyword in the
* "passwd" default file, no generator!
*/
defclose(iop);
return (FILE *)NULL;
}
else if (*progp) {
progp = strdup(progp);
defclose(iop);
/*
* found a value for a possible password generator, now
* check to see if it's a full pathname. If not, error!
*/
if (strncmp(progp, (char *)"/", 1)) {
return (FILE *)NULL;
}
/*
* otherwise, try to "exec" whatever was found in the
* appropriate password generator field.
*/
if ((iop = open_pwgen(progp)) == NULL) {
return (FILE *)NULL;
}
return iop;
}
defclose(iop);
return (FILE *)NULL;
}
/*
* Procedure: open_pwgen
*
* Restrictions:
* fork(2): none
* secsys(2): none
* execl: P_MACREAD (ID-based)
* P_ALLPRIVS (file-based)
*
* Notes: sets up a pipe, forks and executes the password
* generator program. If the exec fails it returns
* a value to indicate that it did fail. On success,
* it returns a file pointer to the pipe.
*/
static FILE *
open_pwgen(progp)
char *progp;
{
register int i;
int pfd[2];
FILE *iop;
(void) pipe(pfd);
if ((i = fork()) < 0) {
(void) close(pfd[0]);
(void) close(pfd[1]);
return (FILE *)NULL;
}
else if (i == 0) {
(void) close(pfd[0]);
(void) close(1);
(void) dup(pfd[1]);
(void) close(pfd[1]);
for (i = 5; i < 15; i++)
(void) close(i);
(void) execl(progp, progp, 0);
(void) close(1);
exit(1); /* tell parent that 'execl' failed */
}
else {
(void) close(pfd[1]);
iop = fdopen(pfd[0], "r");
return iop;
}
/* NOTREACHED */
}
/* diagnostic strings for following routine */
static const char
*tshort = ":898:Password is too short - must be at least %d characters\n",
*nocirc = ":371:Password cannot be circular shift of logonid\n",
*s_char = ":372:Password must contain at least two alphabetic characters\n\tand at least one numeric or special character.\n",
*df3pos = ":373:Passwords must differ by at least 3 positions\n",
*tomany = ":375:Too many tries\n",
*nmatch = ":376:They don't match\n",
*tagain = ":377:Try again.\n";
/*
* Procedure: std_pwgen
*
* Restrictions:
* getpass: none
*
* Notes: does the standard password algorithm checking of the
* user's entry for the requested password. If it succeeds
* the return is 0, otherwise, an error.
*/
static int
std_pwgen(adm, uname, opass, buf, pwbuf)
int adm;
char *uname,
*opass,
*buf,
*pwbuf;
{
register int i, j, k,
pwlen = 0,
count = 0,
opwlen = 0,
insist = 0,
tmpflag, flags, c;
char *pswd,
*p, *o;
cap_t ocap;
cap_value_t cap_audit_write = CAP_AUDIT_WRITE;
opass[8] = '\0';
opwlen = (int) strlen(opass);
while (!strlen(buf)) {
if (insist >= 3) { /* 3 chances to meet triviality */
ocap = cap_acquire(1, &cap_audit_write);
ia_audit("PASSWD", uname, 0,
"Password(s) didn't meet the minimum requirement");
cap_surrender(ocap);
(void)pfmt(stderr, MM_ERROR, ":368:Too many failures\n");
(void)pfmt(stderr, MM_ACTION, again);
return NOPERM;
}
if ((pswd = getpass(gettxt(":369", "New password:"))) == NULL) {
(void) pfmt(stderr, MM_ERROR, sorry);
return FMERR;
}
else {
(void) strncpy(pwbuf, pswd, 8);
pwbuf[8] = '\0';
pwlen = strlen(pwbuf);
}
/*
* Make sure new password is long enough
*/
if (!adm && (pwlen < minlength)) {
(void) pfmt(stderr, MM_ERROR, tshort, minlength);
++insist;
continue;
}
/*
* Check the circular shift of the logonid
*/
if(!adm && circ(uname, pwbuf)) {
(void) pfmt(stderr, MM_ERROR, nocirc);
++insist;
continue;
}
/*
* Insure passwords contain at least two alpha characters
* and one numeric or special character
*/
tmpflag = flags = 0;
p = pwbuf;
if (!adm) {
while (c = *p++) {
if (isalpha(c) && tmpflag )
flags |= 1;
else if (isalpha(c) && !tmpflag ) {
flags |= 2;
tmpflag = 1;
} else if (isdigit(c) )
flags |= 4;
else
flags |= 8;
}
/* 7 = lca,lca,num
* 7 = lca,uca,num
* 7 = uca,uca,num
* 11 = lca,lca,spec
* 11 = lca,uca,spec
* 11 = uca,uca,spec
* 15 = spec,num,alpha,alpha
*/
if ( flags != 7 && flags != 11 && flags != 15 ) {
(void) pfmt(stderr, MM_ERROR, s_char);
++insist;
continue;
}
}
/*
* Old password and New password must differ by at least
* three characters.
*/
if (!adm) {
p = pwbuf;
o = opass;
if (pwlen >= opwlen) {
i = pwlen;
k = pwlen - opwlen;
} else {
i = opwlen;
k = opwlen - pwlen;
}
for (j = 1; j <= i; j++)
if (*p++ != *o++)
k++;
if (k < 3) {
(void) pfmt(stderr, MM_ERROR, df3pos);
++insist;
continue;
}
}
/*
* Ensure password was typed correctly, user gets 3 chances
*/
if ((pswd = getpass(gettxt(":822", "Re-enter new password:"))) == NULL) {
(void) pfmt(stderr, MM_ERROR, sorry);
return FMERR;
}
else {
(void) strncpy(buf, pswd, 8);
buf[8] = '\0';
}
if (strcmp(buf, pwbuf)) {
buf[0] ='\0';
if (++count >= 3) {
(void) pfmt(stderr, MM_ERROR, tomany);
(void) pfmt(stderr, MM_ACTION, again);
return NOPERM;
} else {
(void) pfmt(stderr, MM_ERROR, nmatch);
(void) pfmt(stderr, MM_ACTION, tagain);
}
continue;
}
break; /* terminate "while" loop */
} /* end of "insist" while */
return 0; /* success */
}
/*
* Procedure: call_pwgen
*
* Restrictions:
* fclose: none
* fflush: none
* getpass: none
*
* Notes: does the actual work of reading from the pipe, setting
* up the choices array, and prompting the user for input.
* Also sets the contents of buf and pwbuf for use later.
*/
static void
call_pwgen(iop, buf, pwbuf)
FILE *iop;
char *buf,
*pwbuf;
{
char *pswd,
line[BUFSIZ],
*linep = &line[0],
*choices[MAXSTRS];
int status = 0;
register int i, len,
lcnt = 0,
mlen = 0,
firstime = 1,
todo = MM_INFO;
while ((linep = fgets(linep,sizeof(line),iop)) != NULL) {
if (lcnt > MAXSTRS) {
(void) pfmt(stderr, MM_ERROR, MSG_UF);
(void) fclose(iop);
buf = '\0';
return;
}
/* protect against generators giving us blank lines */
if ( (len = strlen(linep) - 1) <= 0)
continue;
++lcnt;
choices[(lcnt - 1)] = (char *)malloc(len + 1);
(void) strncpy(choices[(lcnt - 1)], linep, len);
choices[(lcnt - 1)][len] = '\0';
}
(void) fflush(iop);
(void) fclose(iop);
(void) wait(&status);
/*
* if the password generator exited with 0 (success), show
* the user the list of available passwords to select.
*/
if (((status >> 8) & 0377) == 0) {
while (lcnt) {
if (!firstime)
todo = MM_ACTION;
firstime = 0;
pfmt(stderr, todo, pwgenmsg);
for (i = 0; i < lcnt; i++) {
(void) printf("\t%s\n", choices[i]);
}
(void) printf("\n");
pswd = getpass(gettxt(":369", "New password:"));
if (!strlen(pswd)) {
(void) pfmt(stderr, MM_ERROR, pwgenerr);
continue;
}
for (i = 0; i < lcnt; i++) {
if ((size_t)strlen(pswd) < (size_t)strlen(choices[i])) {
mlen = 8;
}
else {
mlen = strlen(choices[i]);
}
if (!strncmp(pswd, choices[i], mlen)) {
lcnt = 0;
(void) strcpy(pwbuf, pswd);
(void) strcpy(buf, pswd);
break;
}
}
if (lcnt) {
(void) pfmt(stderr, MM_ERROR, pwgenerr);
}
}
}
else
buf = '\0';
}
/*
* Procedure: init_defaults
*
* Restrictions:
* defopen: none
*
* Notes: opens the default file with the tunables for passwd
* if it can and sets the variables accordingly. If it
* can't open the file (for any reason) it sets the
* variables to "hard" default values defined in the
* source code.
*/
static void
init_defaults()
{
int temp;
char *char_p;
FILE *defltfp;
/*
* set up default valuses if password administration file
* can't be opened, or not all keywords are defined.
*/
login_only = 0;
mindate = MINWEEKS;
maxdate = MAXWEEKS;
warndate = WARNWEEKS;
minlength = MINLENGTH;
historycnt = -1;
historydays = -1;
if ((defltfp = defopen(PWADMIN)) != NULL) { /* M005 start */
if ((char_p = defread(defltfp, "LOGIN_ONLY")) != NULL) {
if (*char_p) {
/*
* if the keyword ``LOGIN_ONLY'' was defined
* and had a value, determine if that value
* was the string ``Yes''. If so, then the
* user can only change the password at login
* time. Otherwise, passwd behaves as always.
*/
if (!strcmp(char_p, "Yes")) {
login_only = 1;
}
}
}
if ((char_p = defread(defltfp, "PASSLENGTH")) != NULL) {
if (*char_p) {
temp = atoi(char_p);
if (temp > 1 && temp < 9) {
minlength = temp;
}
}
}
if ((char_p = defread(defltfp, "MINWEEKS")) != NULL) {
if (*char_p) {
temp = atoi(char_p);
if (temp >= 0) {
mindate = temp * 7;
}
}
}
if ((char_p = defread(defltfp, "WARNWEEKS")) != NULL) {
if (*char_p) {
temp = atoi(char_p);
if (temp >= 0) {
warndate = temp * 7;
}
}
}
if ((char_p = defread(defltfp, "MAXWEEKS")) != NULL) {
if (*char_p) {
if ((temp = atoi(char_p)) == FAIL) {
mindate = -1;
warndate = -1;
}
else if (temp < -1) {
maxdate = MAXWEEKS;
}
else {
maxdate = temp * 7;
}
}
}
if ((char_p = defread(defltfp, "HISTORYCNT")) != NULL) {
if (*char_p) {
if ((temp = atoi(char_p)) == FAIL) {
historycnt = -1;
}
else if (temp < -1) {
historycnt = -1;
/* defaults to not being used */
}
else {
historycnt = temp;
if (historycnt > 25)
historycnt = 25;
/* saints protect us from the admin */
}
}
}
if ((char_p = defread(defltfp, "HISTORYDAYS")) != NULL) {
if (*char_p) {
if ((temp = atoi(char_p)) == FAIL) {
historydays = -1;
}
else if (temp < -1) {
historydays = -1;
/* defaults to not being used */
}
else {
historydays = temp;
/* two years is the max we allow */
if (historydays > 730)
historydays = 730;
}
}
/* if no historycnt given, default it to 25 */
if (historycnt == -1)
historycnt = 25;
}
defclose(defltfp); /* close defaults file */
}
#ifdef DEBUG
(void) printf("init_defaults: maxdate == %d, mindate == %d\n", maxdate, mindate);
#endif
return;
}
static int
update_noshadow(flag, uname)
char *uname;
int flag;
{
register int i, found = 0;
struct stat buf;
struct passwd *pwd;
int fd,
pw_error = 0,
end_of_file = 0;
FILE *tpfp;
char username[32];
int when;
time_t now, maxweeks, minweeks;
/* ignore all the signals */
for (i = 1; i < NSIG; i++)
(void) sigset(i, SIG_IGN);
/* Clear the errno. It is set because SIGKILL can not be ignored. */
errno = 0;
/* Create a temporary file */
(void) unlink(PASSTEMP);
if ((fd = creat(PASSTEMP, 0600)) == FAIL) {
(void) pfmt(stderr, MM_ERROR, MSG_FE);
return FMERR;
}
(void) close(fd);
if ((tpfp = fopen(PASSTEMP, "w")) == NULL) {
(void) pfmt(stderr, MM_ERROR, MSG_FE);
return FMERR;
}
/* copy passwd file to temp, replacing passwd in matching line */
end_of_file = pw_error = 0;
strcpy(username,uname);
setpwent();
while (!end_of_file) {
if ((pwd = getpwent()) != NULL) {
if (strcmp(pwd->pw_name, username) == 0) {
found = 1;
/* LFLAG and DFLAG should be checked before FFLAG.
* FFLAG clears the lastchg field. We do not
* want lastchg field to be set if one execute
* passwd -d -f name or passwd -l -f name.
*/
if (flag & LFLAG) { /* lock password */
pwd->pw_passwd = pw;
if (*pwd->pw_age != NULL) {
CNV_AGE();
when = maxweeks + (minweeks << 6) + (now << 12);
pwd->pw_age = l64a(when);
}
}
if (flag & DFLAG) { /* delete password */
pwd->pw_passwd = nullstr;
if (*pwd->pw_age != NULL) {
CNV_AGE();
when = maxweeks + (minweeks << 6) + (now << 12);
pwd->pw_age = l64a(when);
}
}
if (flag & FFLAG) { /* expire password */
if (*pwd->pw_age != NULL) {
CNV_AGE();
when = maxweeks + (minweeks << 6);
if ( when == 0)
pwd->pw_age = ".";
else
pwd->pw_age = l64a(when);
} else
pwd->pw_age = ".";
}
/* MFLAG should be checked before NFLAG. One
* can not set min field without setting max field.
* If aging is off, force to expire a user password --
* set lastchg field to 0.
*/
if (flag & MFLAG) { /* set max field */
if ( maxdate == -1) {
pwd->pw_age = nullstr;
} else {
if (*pwd->pw_age == NULL) {
minweeks = 0;
when = 0;
} else
CNV_AGE();
maxweeks = (maxdate + (DAY_WEEK -1)) / DAY_WEEK;
maxweeks = maxweeks > DIG_CH ? DIG_CH : maxweeks;
when = maxweeks + (minweeks << 6) + (when & ~0xfff);
if (when == 0)
pwd->pw_age = ".";
else
pwd->pw_age = l64a(when);
}
}
if (flag & NFLAG) { /* set min field */
if (*pwd->pw_age == NULL) {
(void) fprintf(stderr,"%s\n %s", MSG_BS, sausage);
(void) unlink(PASSTEMP);
return (BADSYN);
}
CNV_AGE();
minweeks = (mindate + (DAY_WEEK - 1)) / DAY_WEEK;
minweeks = minweeks > DIG_CH ? DIG_CH : minweeks;
when = maxweeks + (minweeks << 6) + (when & ~0xfff);
if (when == 0)
pwd->pw_age = ".";
else
pwd->pw_age = l64a(when);
}
if (flag & PFLAG) { /* change password */
pwd->pw_passwd = pw;
if (*pwd->pw_age != NULL) {
CNV_AGE();
if (maxweeks == 0) { /* turn off aging */
pwd->pw_age = nullstr;
} else {
when = maxweeks + (minweeks << 6) + (now << 12);
pwd->pw_age = l64a(when);
}
}
}
}
if (putpwent (pwd, tpfp) != 0) {
endpwent();
(void) unlink(PASSTEMP);
(void) pfmt(stderr, MM_ERROR, MSG_FE);
return FMERR;
}
} else {
if (errno == 0)
/* end of file */
end_of_file = 1;
else if (errno == EINVAL) {
/*bad entry found in /etc/passwd, skip it */
errno = 0;
pw_error++;
} else
/* unexpected error found */
end_of_file = 1;
}
} /*end of while*/
endpwent();
if (pw_error >= 1)
pfmt(stderr, MM_ERROR, badentry);
if (fclose (tpfp)) {
(void) unlink(PASSTEMP);
(void) pfmt(stderr, MM_ERROR, MSG_FE);
return FMERR;
}
/* Check if user name exists */
if (!found) {
(void) unlink(PASSTEMP);
(void) pfmt(stderr, MM_ERROR, MSG_FE);
return FMERR;
}
/* change mode, owner & group of temp file to mode of passwd file */
if (stat(PASSWD, &buf) < 0) {
(void) unlink(PASSTEMP);
(void) pfmt(stderr, MM_ERROR, MSG_FE);
return FMERR;
}
if (chmod(PASSTEMP, buf.st_mode) != SUCCESS) {
(void) unlink(PASSTEMP);
(void) pfmt(stderr, MM_ERROR, MSG_FE);
return FMERR;
}
if (chown(PASSTEMP, buf.st_uid, buf.st_gid) != SUCCESS) {
(void) unlink(PASSTEMP);
(void) pfmt(stderr, MM_ERROR, MSG_FE);
return FMERR;
}
/* Rename temp file back to appropriate passwd file and return */
if (access(PASSWD, 0) == 0) {
if (access(OPASSWD, 0) == 0) {
if (unlink(OPASSWD) < 0) {
(void) unlink(PASSTEMP);
(void) pfmt(stderr, MM_ERROR, MSG_FE);
return FMERR;
}
}
if (link(PASSWD, OPASSWD) < 0 ) {
(void) unlink(PASSTEMP);
(void) pfmt(stderr, MM_ERROR, MSG_FE);
return FMERR;
}
}
if (rename(PASSTEMP, PASSWD) < 0 ) {
(void) unlink(PASSWD);
if (link(OPASSWD, PASSWD) < 0 ) {
(void) pfmt(stderr, MM_ERROR, MSG_FF);
return FATAL;
}
(void) pfmt(stderr, MM_ERROR, MSG_FE);
return FMERR;
}
/* let NSD know about the change */
rmdir("/ns/.local/passwd.byname");
rmdir("/ns/.local/passwd.byuid");
return SUCCESS;
}
/*
* Procedure: get_pwvalidate
*
* Restrictions:
* defopen: none
*
* Notes: does validation for determining what the password
* validator is, reads the default passwd file, and
* "exec" the password validation pgm if it exists.
* This routine calls "open_pwvalidate" that creates a pipe and
* does a "fork()" and "exec()".
*/
static FILE *
get_pwvalidate()
{
char prog[BUFSIZ],
passgen[BUFSIZ],
*progp = &prog[0],
*pstrp = &passgen[0];
FILE *iop;
/* borrowed this from the generator invocation */
progp = '\0';
(void) strcpy(pstrp, "PASSWDVALIDATE");
if ((iop = defopen(PWADMIN)) == NULL) {
/*
* couldn't open "passwd" default file, no validation pgm!
*/
return (FILE *)NULL;
}
if ((progp = defread(iop, pstrp)) == NULL) {
/*
* didn't find the password validator keyword in the
* "passwd" default file, no validator!
*/
defclose(iop);
return (FILE *)NULL;
}
else if (*progp) {
progp = strdup(progp);
defclose(iop);
/*
* found a value for a possible password validator, now
* check to see if it's a full pathname. If not, error!
*/
if (strncmp(progp, (char *)"/", 1)) {
return (FILE *)NULL;
}
/*
* otherwise, try to "exec" whatever was found in the
* appropriate password validator field.
*/
if ((iop = open_pwval(progp)) == NULL) {
return (FILE *)NULL;
}
return iop;
}
defclose(iop);
return (FILE *)NULL;
}
/*
* Procedure: call_pwvalidate
*
* Notes: writes proposed passwd down the pipe to the external
* validation pgm, then waits for exit and returns status.
*/
static int
call_pwvalidate(iop, pwbuf)
FILE *iop;
char *pwbuf;
{
int status;
(void) fprintf(iop,"%s\n",pwbuf);
(void) fflush(iop);
(void) fclose(iop);
(void) wait(&status);
return(status);
}
/*
* Procedure: open_pwval
*
* Notes: sets up a pipe, forks and executes the password
* validator program. If the exec fails it returns
* a value to indicate that it did fail. On success,
* it returns a file pointer to the pipe.
* This was derived from open_pwgen, but needs to write vs
* read the pipe.
*/
static FILE *
open_pwval(progp)
char *progp;
{
register int i;
int pfd[2];
FILE *iop;
(void) pipe(pfd);
if ((i = fork()) < 0) {
(void) close(pfd[0]);
(void) close(pfd[1]);
return (FILE *)NULL;
}
else if (i == 0) {
(void) close(pfd[1]);
(void) close(0);
(void) dup(pfd[0]);
(void) close(pfd[0]);
for (i = 5; i < 15; i++)
(void) close(i);
(void) execl(progp, progp, 0);
(void) close(0);
exit(1); /* tell parent that 'execl' failed */
}
else {
(void) close(pfd[0]);
iop = fdopen(pfd[1], "w");
return iop;
}
/* NOTREACHED */
}
/*
* Procedure: ck_pwhistory(user, clear_passwd)
* Returns: SUCCESS if the password is OK (not used previously)
* error code if it has been used in the last HISTORYCNT passwd changes
*
* Notes: if the site has configured HISTORYCNT to a non zero value
* we check to see that this password doesn't match the last
* HISTORYCNT values
* The format of the file is:
* username:encrypted1:day_it_chgd:encrypted2:day_it_chgd:...
* where the newest encrypted passwd used is encrypted1
* day_it_chgd is the DAY_NOW value when the encryptedX
* was changed. This is used in HISTORYDAYS processing.
*/
static int
ck_pwhistory(user, pw)
char * user;
char * pw; /* clear passwd */
{
FILE *hfp;
char line[2048];
char *linep,*pf,*pf2;
int match;
char wrkpwbuf[10];
int now = DAY_NOW;
int chgd;
/* don't care about reuse, default */
if (historycnt == -1 && historydays == -1 )
return SUCCESS;
if ( (hfp = fopen(PWHISTORY,"r")) ==NULL)
{
/* no history file, so de facto OK */
return SUCCESS;
}
linep = &line[0];
while ((linep = fgets(linep,sizeof(line),hfp)) != NULL)
{
/* skip comment lines */
if (*linep == '#') continue;
pf = strtok(linep,":\n");
/* is this the name we're looking for? */
if ( strcmp(pf,user) != 0) continue;
/*now see if the password matches a previous value */
while ( (pf=strtok(NULL,":\n")) != NULL)
{
if (strcmp(crypt(pw,pf),pf) == 0)
{
fclose(hfp);
fprintf(stderr,"Sorry, you have reused an old password\n");
return 1;
}
/* date it changed */
if ( (pf2 = strtok(NULL,":\n")) == NULL)
break;
/* check for date of change, stop if we encounter one older
* than historydays ago
*/
if ( historydays != -1)
{
/* the checks are for potential corruption of history file */
if ( ((chgd = atoi(pf2)) <= 0) || (chgd > 25000) )
break;
if ( (now - chgd) > historydays)
break;
}
}
fclose(hfp);
return SUCCESS; /* didn't match any prior pw */
}
fclose(hfp);
return SUCCESS; /* never found user in the file */
}
/* update the history file*/
static int
update_pwhistory(user, pw)
char * user;
char * pw; /* encrypted passwd */
{
FILE *hfp, *tfp;
int fd, i, glbl_match;
char line[2048];
char *linep;
char namebuf[16];
int match;
struct stat buf;
char *pf,*pn,*pf2;
int now;
int chgd;
/* don't care about reuse, default */
if (historycnt == -1 && historydays == -1 )
return SUCCESS;
if ( (hfp = fopen(PWHISTORY,"r")) ==NULL)
{
hfp = fopen("/dev/null","r"); /* no history file, fake it */
}
/* Create a temporary file */
(void) unlink(PWHISTTEMP);
if ((fd = creat(PWHISTTEMP, 0600)) == FAIL) {
(void) pfmt(stderr, MM_ERROR, MSG_FE);
return FMERR;
}
(void) close(fd);
if ((tfp = fopen(PWHISTTEMP, "w")) == NULL) {
(void) pfmt(stderr, MM_ERROR, MSG_FE);
return FMERR;
}
now = DAY_NOW;
linep = &line[0];
glbl_match = 0;
while ((linep = fgets(linep,sizeof(line),hfp)) != NULL)
{
/* replicate comment lines */
if (*linep == '#')
{
fputs(linep,tfp);
continue;
}
/* if the name doesn't match, just echo the line */
/* don't want strtok to munch on this line just yet */
for (pf = linep, pn=&namebuf[0];
((*pf != ':') && (*pf != '\0') && (*pf != '\n')); pf++,pn++)
{
*pn = *pf;
}
*pn = '\0';
if (strcmp((char *)&namebuf[0],user) != 0)
{
fputs(linep,tfp);
continue;
}
/* now it's OK to munch the line, we're tossing the first field */
pf = strtok(linep,":\n");
glbl_match = 1; /* we found the user */
/* matched the user name, update the history */
fprintf(tfp,"%s:%s:%d:",user,pw,now);
/* put out the other saved passwds, up to the max configured */
for (i = 2; i <= historycnt; i++)
{
if ( (pf = strtok(NULL,":\n")) == NULL)
break;
/* date it changed */
if ( (pf2 = strtok(NULL,":\n")) == NULL)
break;
/* check for date of change, stop if we encounter one older
* than historydays ago
*/
if ( historydays != -1)
{
/* the checks are for potential corruption of history file */
if ( ((chgd = atoi(pf2)) <= 0) || (chgd > 25000) )
break;
if ( (now - chgd) > historydays)
break;
}
fprintf(tfp,"%s:%s:",pf,pf2);
}
fprintf(tfp,"\n");
}
/* never found the user in the history file, so write a new entry */
if (glbl_match == 0)
{
fprintf(tfp,"%s:%s:%d:\n",user,pw,now);
}
fclose(hfp);
fclose(tfp);
/* change mode, owner & group of temp file to proper values */
if (stat(PASSWD, &buf) < 0) {
(void) unlink(PWHISTTEMP);
(void) pfmt(stderr, MM_ERROR, MSG_FE);
return FMERR;
}
if (chmod(PWHISTTEMP, 0400) != SUCCESS) {
(void) unlink(PWHISTTEMP);
(void) pfmt(stderr, MM_ERROR, MSG_FE);
return FMERR;
}
if (chown(PWHISTTEMP, buf.st_uid, buf.st_gid) != SUCCESS) {
(void) unlink(PWHISTTEMP);
(void) pfmt(stderr, MM_ERROR, MSG_FE);
return FMERR;
}
/* Rename temp file back to appropriate passwd file and return */
if (rename(PWHISTTEMP, PWHISTORY) < 0 ) {
(void) pfmt(stderr, MM_ERROR, MSG_FE);
return FMERR;
}
return SUCCESS;
}
#ifdef DEBUG
lckpwdf()
{
return(0);
}
ulkpwdf()
{
return(0);
}
#endif