#include <pwd.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <shadow.h>
#include <grp.h>
#include <signal.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <locale.h>
#include <string.h>
#define PRIVILEGED 0
#define SUCCESS 0
#define NOPERM 1
#define BADSYN 2
#define FMERR 3
#define FATAL 4
#define FBUSY 5
#define BADSHW 6
#define DELPTMP() (void) unlink(PASSTEMP)
#define DELSHWTMP() (void) unlink(SHADTEMP)
char pwdflr[] = "x";
char *prognamp;
void f_err(void), f_miss(void), f_bdshw(void);
struct spwd *
local_getspnam(char *name)
{
FILE *shadf;
struct spwd *sp;
if ((shadf = fopen("/etc/shadow", "r")) == NULL)
return (NULL);
while ((sp = fgetspent(shadf)) != NULL) {
if (strcmp(sp->sp_namp, name) == 0)
break;
}
(void) fclose(shadf);
return (sp);
}
int
main(int argc, char **argv)
{
extern int errno;
void no_recover(void), no_convert(void);
struct passwd *pwdp;
struct spwd *sp, sp_pwd;
struct stat buf;
FILE *tp_fp, *tsp_fp;
time_t when, minweeks, maxweeks;
int file_exist = 1;
int end_of_file = 0;
mode_t mode;
mode_t pwd_mode;
int pwerr = 0;
ushort_t i;
gid_t pwd_gid, sp_gid;
uid_t pwd_uid, sp_uid;
FILE *pwf;
int black_magic = 0;
int count;
(void) setlocale(LC_ALL, "");
#if !defined(TEXT_DOMAIN)
#define TEXT_DOMAIN "SYS_TEST"
#endif
(void) textdomain(TEXT_DOMAIN);
prognamp = argv[0];
if (getuid() != PRIVILEGED) {
(void) fprintf(stderr, gettext("%s: Permission denied.\n"),
prognamp);
exit(NOPERM);
}
if (argc > 1) {
(void) fprintf(stderr,
gettext("%s: Invalid command syntax.\n"), prognamp);
(void) fprintf(stderr, gettext("Usage: pwconv\n"));
exit(BADSYN);
}
if (lckpwdf() < 0) {
(void) fprintf(stderr,
gettext("%s: Password file(s) busy. Try again later.\n"),
prognamp);
exit(FBUSY);
}
for (i = 1; i < NSIG; i++)
(void) sigset(i, SIG_IGN);
errno = 0;
if (stat(PASSWD, &buf) < 0) {
(void) f_miss();
exit(FATAL);
}
pwd_gid = buf.st_gid;
pwd_uid = buf.st_uid;
pwd_mode = buf.st_mode;
(void) umask(S_IAMB & ~(buf.st_mode & (S_IRUSR|S_IRGRP|S_IROTH)));
if ((tp_fp = fopen(PASSTEMP, "w")) == NULL) {
(void) f_err();
exit(FMERR);
}
if (chown(PASSTEMP, pwd_uid, pwd_gid) < 0) {
DELPTMP();
(void) f_err();
exit(FMERR);
}
mode = S_IAMB & ~(S_IRUSR);
if (access(SHADOW, F_OK) == 0) {
if (stat(SHADOW, &buf) == 0) {
mode = S_IAMB & ~(buf.st_mode & S_IRUSR);
sp_gid = buf.st_gid;
sp_uid = buf.st_uid;
} else {
DELPTMP();
(void) f_err();
exit(FMERR);
}
} else {
sp_gid = pwd_gid;
sp_uid = pwd_uid;
file_exist = 0;
}
(void) umask(mode);
if ((tsp_fp = fopen(SHADTEMP, "w")) == NULL) {
DELPTMP();
(void) f_err();
exit(FMERR);
}
if (chown(SHADTEMP, sp_uid, sp_gid) < 0) {
(void) no_convert();
exit(FMERR);
}
if ((pwf = fopen("/etc/passwd", "r")) == NULL) {
no_recover();
exit(FATAL);
}
count = 0;
while (!end_of_file) {
count++;
if ((pwdp = fgetpwent(pwf)) != NULL) {
if (!file_exist ||
(sp = local_getspnam(pwdp->pw_name)) == NULL) {
if (errno == EINVAL) {
DELSHWTMP();
DELPTMP();
(void) f_bdshw();
exit(BADSHW);
}
sp = &sp_pwd;
sp->sp_namp = pwdp->pw_name;
if (!pwdp->pw_passwd ||
(pwdp->pw_passwd &&
*pwdp->pw_passwd == '\0')) {
(void) fprintf(stderr, gettext(
"%s: WARNING user %s has no "
"password\n"),
prognamp, sp->sp_namp);
}
sp->sp_pwdp = pwdp->pw_passwd;
pwdp->pw_passwd = pwdflr;
if (pwdp->pw_age && *pwdp->pw_age != 0) {
when = (long)a64l(pwdp->pw_age);
maxweeks = when & 077;
minweeks = (when >> 6) & 077;
when >>= 12;
sp->sp_lstchg = when * 7;
sp->sp_min = minweeks * 7;
sp->sp_max = maxweeks * 7;
sp->sp_warn = -1;
sp->sp_inact = -1;
sp->sp_expire = -1;
sp->sp_flag = 0;
pwdp->pw_age = "";
} else {
sp->sp_lstchg = DAY_NOW;
sp->sp_min = -1;
sp->sp_max = -1;
sp->sp_warn = -1;
sp->sp_inact = -1;
sp->sp_expire = -1;
sp->sp_flag = 0;
}
} else {
black_magic = (*pwdp->pw_name == '+' ||
*pwdp->pw_name == '-');
if ((!pwdp->pw_passwd ||
(pwdp->pw_passwd &&
*pwdp->pw_passwd == '\0')) &&
!black_magic) {
(void) fprintf(stderr, gettext(
"%s: WARNING user %s has no "
"password\n"),
prognamp, sp->sp_namp);
}
if (pwdp->pw_passwd && *pwdp->pw_passwd) {
if (strcmp(pwdp->pw_passwd, pwdflr)) {
sp->sp_pwdp = pwdp->pw_passwd;
pwdp->pw_passwd = pwdflr;
if (!pwdp->pw_age ||
(pwdp->pw_age &&
*pwdp->pw_age == 0)) {
sp->sp_lstchg = DAY_NOW;
sp->sp_min = -1;
sp->sp_max = -1;
sp->sp_warn = -1;
sp->sp_inact = -1;
sp->sp_expire = -1;
sp->sp_flag = 0;
}
}
} else {
sp->sp_pwdp = "";
pwdp->pw_passwd = pwdflr;
sp->sp_lstchg = sp->sp_min =
sp->sp_max = -1;
sp->sp_warn = sp->sp_inact =
sp->sp_expire = -1;
sp->sp_flag = 0;
}
if (pwdp->pw_age && *pwdp->pw_age != '\0') {
when = (long)a64l(pwdp->pw_age);
maxweeks = when & 077;
minweeks = (when >> 6) & 077;
when >>= 12;
sp->sp_lstchg = when * 7;
sp->sp_min = minweeks * 7;
sp->sp_max = maxweeks * 7;
sp->sp_warn = -1;
sp->sp_inact = -1;
sp->sp_expire = -1;
sp->sp_flag = 0;
pwdp->pw_age = "";
}
}
if ((putpwent(pwdp, tp_fp)) != 0) {
(void) no_convert();
exit(FMERR);
}
if (putspent(sp, tsp_fp) != 0) {
(void) no_convert();
exit(FMERR);
}
} else {
if (feof(pwf)) {
end_of_file = 1;
} else {
errno = 0;
pwerr = 1;
(void) fprintf(stderr,
gettext("%s: ERROR: bad entry or blank "
"line at line %d in /etc/passwd\n"),
prognamp, count);
}
}
}
(void) fclose(pwf);
(void) fclose(tsp_fp);
(void) fclose(tp_fp);
if (pwerr) {
(void) no_convert();
exit(FMERR);
}
if (unlink(OPASSWD) && (access(OPASSWD, F_OK) == 0)) {
(void) no_convert();
exit(FMERR);
}
if (rename(PASSWD, OPASSWD) == -1) {
(void) no_convert();
exit(FMERR);
}
if (rename(PASSTEMP, PASSWD) == -1) {
if (link(OPASSWD, PASSWD) < 0) {
(void) no_recover();
exit(FATAL);
}
(void) no_convert();
exit(FMERR);
}
if (unlink(OSHADOW) && (access(OSHADOW, R_OK) == 0)) {
if (unlink(PASSWD) || link(OPASSWD, PASSWD)) {
(void) no_recover();
exit(FATAL);
}
(void) no_convert();
exit(FMERR);
}
if (file_exist && rename(SHADOW, OSHADOW)) {
if (unlink(PASSWD) || link(OPASSWD, PASSWD)) {
(void) no_recover();
exit(FATAL);
}
(void) no_convert();
exit(FMERR);
}
if (rename(SHADTEMP, SHADOW) == -1) {
if (file_exist && (link(OSHADOW, SHADOW))) {
(void) no_recover();
exit(FATAL);
}
if (unlink(PASSWD) || link(OPASSWD, PASSWD)) {
(void) no_recover();
exit(FATAL);
}
(void) no_convert();
exit(FMERR);
}
(void) chmod(PASSWD, pwd_mode);
if (chmod(OPASSWD, S_IRUSR) < 0)
(void) unlink(OPASSWD);
(void) ulckpwdf();
return (0);
}
void
no_recover(void)
{
DELPTMP();
DELSHWTMP();
(void) f_miss();
}
void
no_convert(void)
{
DELPTMP();
DELSHWTMP();
(void) f_err();
}
void
f_err(void)
{
(void) fprintf(stderr,
gettext("%s: Unexpected failure. Conversion not done.\n"),
prognamp);
(void) ulckpwdf();
}
void
f_miss(void)
{
(void) fprintf(stderr,
gettext("%s: Unexpected failure. Password file(s) missing.\n"),
prognamp);
(void) ulckpwdf();
}
void
f_bdshw(void)
{
(void) fprintf(stderr,
gettext("%s: Bad entry in /etc/shadow. Conversion not done.\n"),
prognamp);
(void) ulckpwdf();
}