#include <stdio.h>
#include <pwd.h>
#include <sys/types.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <locale.h>
#include <stdarg.h>
#include <errno.h>
#include <string.h>
#include <security/pam_appl.h>
#include <security/pam_modules.h>
#include <security/pam_impl.h>
#include <rpcsvc/nis.h>
#undef GROUP
#include <syslog.h>
#include <userdefs.h>
#include <passwdutil.h>
#include <nss_dbdefs.h>
#include <deflt.h>
#undef GROUP
#include <bsm/adt.h>
#include <bsm/adt_event.h>
#define LFLAG 0x001
#define DFLAG 0x002
#define MFLAG 0x004
#define NFLAG 0x008
#define SFLAG 0x010
#define FFLAG 0x020
#define AFLAG 0x040
#define SAFLAG (SFLAG|AFLAG)
#define WFLAG 0x100
#define OFLAG 0x200
#define EFLAG 0x400
#define GFLAG 0x800
#define HFLAG 0x1000
#define XFLAG 0x2000
#define UFLAG 0x4000
#define NONAGEFLAG (EFLAG | GFLAG | HFLAG)
#define AGEFLAG (LFLAG | FFLAG | MFLAG | NFLAG | WFLAG | XFLAG | UFLAG)
#define MUTEXFLAG (DFLAG | LFLAG | XFLAG | UFLAG | SAFLAG)
#define SUCCESS 0
#define NOPERM 1
#define BADOPT 2
#define FMERR 3
#define FATAL 4
#define FBUSY 5
#define BADSYN 6
#define BADAGE 7
#define NOMEM 8
#define SYSERR 9
#define EXPIRED 10
#define MSG_NP "Permission denied"
#define MSG_BS "Invalid combination of options"
#define MSG_FE "Unexpected failure. Password file/table unchanged."
#define MSG_FF "Unexpected failure. Password file/table missing."
#define MSG_FB "Password file/table busy. Try again later."
#define MSG_NV "Invalid argument to option"
#define MSG_AD "Password aging is disabled"
#define MSG_RS "Cannot change from restricted shell %s\n"
#define MSG_NM "Out of memory."
#define MSG_UNACCEPT "%s is unacceptable as a new shell\n"
#define MSG_UNAVAIL "warning: %s is unavailable on this machine\n"
#define MSG_COLON "':' is not allowed.\n"
#define MSG_MAXLEN "Maximum number of characters allowed is %d."
#define MSG_CONTROL "Control characters are not allowed.\n"
#define MSG_SHELL_UNCHANGED "Login shell unchanged.\n"
#define MSG_GECOS_UNCHANGED "Finger information unchanged.\n"
#define MSG_DIR_UNCHANGED "Homedir information unchanged.\n"
#define MSG_NAME "\nName [%s]: "
#define MSG_HOMEDIR "\nHome Directory [%s]: "
#define MSG_OLDSHELL "Old shell: %s\n"
#define MSG_NEWSHELL "New shell: "
#define MSG_AGAIN "\nPlease try again\n"
#define MSG_INPUTHDR "Default values are printed inside of '[]'.\n" \
"To accept the default, type <return>.\n" \
"To have a blank entry, type the word 'none'.\n"
#define MSG_UNKNOWN "%s: User unknown: %s\n"
#define MSG_ACCOUNT_EXP "User account has expired: %s\n"
#define MSG_AUTHTOK_EXP "Your password has been expired for too long.\n" \
"Please contact the system administrator.\n"
#define MSG_NIS_HOMEDIR "-h does not apply to NIS"
#define MSG_CUR_PASS "Enter existing login password: "
#define MSG_CUR_PASS_UNAME "Enter %s's existing login password: "
#define MSG_SUCCESS "%s: password information changed for %s\n"
#define MSG_SORRY "%s: Sorry, wrong passwd\n"
#define MSG_INFO "%s: Changing password for %s\n"
#define FAIL -1
#define PASSWD "/etc/passwd"
#define MAX_INPUT_LEN 512
#define DEF_ATTEMPTS 3
#define NUMCP 13
#ifdef DEBUG
#define dprintf1 printf
#else
#define dprintf1(w, x)
#endif
extern int optind;
static int retval = SUCCESS;
static int pam_retval = PAM_SUCCESS;
static uid_t uid;
static char *prognamep;
static long maxdate;
static int passwd_conv(int, const struct pam_message **,
struct pam_response **, void *);
static struct pam_conv pam_conv = {passwd_conv, NULL};
static pam_handle_t *pamh;
static char *usrname;
static adt_session_data_t *ah;
static adt_event_data_t *event = NULL;
static pam_repository_t auth_rep;
static pwu_repository_t repository;
static pwu_repository_t __REPFILES = { "files", NULL, 0 };
extern void setusershell(void);
extern char *getusershell(void);
extern void endusershell(void);
static void passwd_exit(int retcode) __NORETURN;
static void rusage(void);
static int ckuid(void);
static int ckarg(int argc, char **argv, attrlist **attributes);
static int get_namelist(pwu_repository_t, char ***, int *);
static int get_namelist_files(char ***, int *);
static int get_namelist_local(char ***, int *);
static int get_attr(char *, pwu_repository_t *, attrlist **);
static void display_attr(char *, attrlist *);
static void free_attr(attrlist *);
static void attrlist_add(attrlist **, attrtype, char *);
static void attrlist_reorder(attrlist **);
static char *userinput(char *, pwu_repository_t *, attrtype);
static char *getresponse(char *);
int
main(int argc, char *argv[])
{
int flag;
char **namelist;
int num_user;
int i;
attrlist *attributes = NULL;
char *input;
int tries = 1;
int updated_reps;
if ((prognamep = strrchr(argv[0], '/')) != NULL)
++prognamep;
else
prognamep = argv[0];
auth_rep.type = NULL;
auth_rep.scope = NULL;
repository.type = NULL;
repository.scope = NULL;
repository.scope_len = 0;
i = 0;
flag = 0;
uid = getuid();
(void) setlocale(LC_ALL, "");
#if !defined(TEXT_DOMAIN)
#define TEXT_DOMAIN "SYS_TEST"
#endif
(void) textdomain(TEXT_DOMAIN);
flag = ckarg(argc, argv, &attributes);
dprintf1("flag is %0x\n", flag);
if (flag == FAIL)
passwd_exit(retval);
argc -= optind;
if (argc < 1) {
if ((usrname = getlogin()) == NULL) {
struct passwd *pass = getpwuid(uid);
if (pass != NULL)
usrname = pass->pw_name;
else {
rusage();
exit(NOPERM);
}
} else if (flag == 0) {
(void) fprintf(stderr, gettext(MSG_INFO), prognamep,
usrname);
}
} else {
usrname = argv[optind];
}
if (pam_start("passwd", usrname, &pam_conv, &pamh) != PAM_SUCCESS) {
passwd_exit(NOPERM);
}
auth_rep.type = repository.type;
auth_rep.scope = repository.scope;
auth_rep.scope_len = repository.scope_len;
if (auth_rep.type != NULL) {
if (pam_set_item(pamh, PAM_REPOSITORY, (void *)&auth_rep)
!= PAM_SUCCESS) {
passwd_exit(NOPERM);
}
}
if (flag == SAFLAG) {
retval = get_namelist(repository, &namelist, &num_user);
if (retval != SUCCESS)
(void) passwd_exit(retval);
if (num_user == 0) {
(void) fprintf(stderr, "%s: %s\n", prognamep,
gettext(MSG_FF));
passwd_exit(FATAL);
}
i = 0;
while (namelist[i] != NULL) {
(void) get_attr(namelist[i], &repository,
&attributes);
(void) display_attr(namelist[i], attributes);
(void) free(namelist[i]);
(void) free_attr(attributes);
i++;
}
(void) free(namelist);
passwd_exit(SUCCESS);
} else if (flag == SFLAG) {
if (get_attr(usrname, &repository, &attributes) ==
PWU_SUCCESS) {
(void) display_attr(usrname, attributes);
(void) free_attr(attributes);
}
passwd_exit(SUCCESS);
}
switch (pam_authenticate(pamh, 0)) {
case PAM_SUCCESS:
break;
case PAM_USER_UNKNOWN:
(void) fprintf(stderr, gettext(MSG_UNKNOWN), prognamep,
usrname);
passwd_exit(NOPERM);
break;
case PAM_PERM_DENIED:
passwd_exit(NOPERM);
break;
case PAM_AUTH_ERR:
(void) fprintf(stderr, gettext(MSG_SORRY), prognamep);
passwd_exit(NOPERM);
break;
default:
passwd_exit(FMERR);
break;
}
if (flag == 0) {
int chk_authtok = 0;
dprintf1("call pam_chauthtok() repository name =%s\n",
repository.type);
if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA) != 0) {
perror("adt_start_session");
passwd_exit(SYSERR);
}
if ((event = adt_alloc_event(ah, ADT_passwd)) == NULL) {
perror("adt_alloc_event");
passwd_exit(NOMEM);
}
if (ckuid() != SUCCESS) {
pam_retval = pam_acct_mgmt(pamh, PAM_SILENT);
switch (pam_retval) {
case PAM_ACCT_EXPIRED:
(void) fprintf(stderr,
gettext(MSG_ACCOUNT_EXP), usrname);
passwd_exit(EXPIRED);
break;
case PAM_AUTHTOK_EXPIRED:
(void) fprintf(stderr,
gettext(MSG_AUTHTOK_EXP));
passwd_exit(NOPERM);
break;
case PAM_NEW_AUTHTOK_REQD:
break;
case PAM_SUCCESS:
break;
default:
passwd_exit(NOPERM);
}
}
pam_retval = PAM_AUTHTOK_ERR;
tries = 1;
if (ckuid() == SUCCESS) {
chk_authtok = PAM_NO_AUTHTOK_CHECK;
}
while (pam_retval == PAM_AUTHTOK_ERR && tries <= DEF_ATTEMPTS) {
if (tries > 1)
(void) printf(gettext(MSG_AGAIN));
pam_retval = pam_chauthtok(pamh, chk_authtok);
if (pam_retval == PAM_TRY_AGAIN) {
(void) sleep(1);
pam_retval = pam_chauthtok(pamh, chk_authtok);
}
tries++;
}
switch (pam_retval) {
case PAM_SUCCESS:
retval = SUCCESS;
break;
case PAM_AUTHTOK_DISABLE_AGING:
retval = BADAGE;
break;
case PAM_AUTHTOK_LOCK_BUSY:
retval = FBUSY;
break;
case PAM_TRY_AGAIN:
retval = FBUSY;
break;
case PAM_AUTHTOK_ERR:
case PAM_AUTHTOK_RECOVERY_ERR:
default:
retval = NOPERM;
break;
}
(void) passwd_exit(retval);
} else {
switch (flag) {
case EFLAG:
input = userinput(usrname, &repository, ATTR_SHELL);
if (input)
attrlist_add(&attributes, ATTR_SHELL, input);
else
(void) printf(gettext(MSG_SHELL_UNCHANGED));
break;
case GFLAG:
input = userinput(usrname, &repository, ATTR_GECOS);
if (input)
attrlist_add(&attributes, ATTR_GECOS, input);
else
(void) printf(gettext(MSG_GECOS_UNCHANGED));
break;
case HFLAG:
input = userinput(usrname, &repository, ATTR_HOMEDIR);
if (input)
attrlist_add(&attributes, ATTR_HOMEDIR, input);
else
(void) printf(gettext(MSG_DIR_UNCHANGED));
break;
}
if (attributes != NULL) {
retval = __set_authtoken_attr(usrname,
pamh->ps_item[PAM_AUTHTOK].pi_addr,
&repository, attributes, &updated_reps);
switch (retval) {
case PWU_SUCCESS:
for (i = 1; i <= REP_LAST; i <<= 1) {
if ((updated_reps & i) == 0)
continue;
(void) printf(gettext(MSG_SUCCESS),
prognamep, usrname);
}
retval = SUCCESS;
break;
case PWU_AGING_DISABLED:
retval = BADAGE;
break;
default:
retval = NOPERM;
break;
}
} else {
retval = SUCCESS;
}
(void) passwd_exit(retval);
}
return (0);
}
char *
getresponse(char *oldval)
{
char resp[MAX_INPUT_LEN];
char *retval = NULL;
int resplen;
(void) fgets(resp, sizeof (resp) - 1, stdin);
resplen = strlen(resp) - 1;
if (resp[resplen] == '\n')
resp[resplen] = '\0';
if (*resp != '\0' && strcmp(resp, oldval) != 0)
retval = strdup(resp);
return (retval);
}
char *
userinput(char *name, pwu_repository_t *rep, attrtype type)
{
attrlist oldattr;
char *oldval;
char *valid;
char *response;
char *cp;
oldattr.type = type;
oldattr.next = NULL;
if (__get_authtoken_attr(name, rep, &oldattr) != PWU_SUCCESS)
passwd_exit(FMERR);
oldval = oldattr.data.val_s;
if (type == ATTR_SHELL) {
if (*oldval == '\0') {
free(oldval);
oldval = strdup(DEFSHL);
}
if (ckuid() != SUCCESS) {
setusershell();
valid = getusershell();
while (valid && strcmp(valid, oldval) != 0)
valid = getusershell();
endusershell();
if (valid == NULL) {
(void) fprintf(stderr, gettext(MSG_RS), oldval);
free(oldval);
return (NULL);
}
}
(void) printf(gettext(MSG_OLDSHELL), oldval);
(void) printf(gettext(MSG_NEWSHELL));
(void) fflush(stdout);
response = getresponse(oldval);
free(oldval);
if (response == NULL || *response == '\0')
return (NULL);
setusershell();
valid = getusershell();
while (valid) {
char *cp;
if (*response == '/') {
cp = valid;
} else {
if ((cp = strrchr(valid, '/')) == NULL)
cp = valid;
else
cp++;
}
if (strcmp(cp, response) == 0) {
if (*response != '/') {
free(response);
response = strdup(valid);
}
break;
}
valid = getusershell();
}
endusershell();
if (valid == NULL) {
(void) fprintf(stderr, gettext(MSG_UNACCEPT), response);
return (NULL);
}
if (access(response, X_OK) < 0)
(void) fprintf(stderr, gettext(MSG_UNAVAIL), response);
return (response);
}
(void) printf(gettext(MSG_INPUTHDR));
for (;;) {
if (type == ATTR_GECOS)
(void) printf(gettext(MSG_NAME), oldval);
else if (type == ATTR_HOMEDIR)
(void) printf(gettext(MSG_HOMEDIR), oldval);
response = getresponse(oldval);
if (response && strcmp(response, "none") == 0)
*response = '\0';
if (response == NULL || *response == '\0')
break;
if (strchr(response, ':')) {
(void) fprintf(stderr, "%s", gettext(MSG_COLON));
free(response);
} else if (strlen(response) > MAX_INPUT_LEN - 1) {
(void) fprintf(stderr, gettext(MSG_MAXLEN),
MAX_INPUT_LEN);
free(response);
} else {
for (cp = response; *cp >= 040; cp++)
;
if (*cp != '\0') {
(void) fprintf(stderr, gettext(MSG_CONTROL));
free(response);
} else
break;
}
}
free(oldval);
return (response);
}
static int
ckarg(int argc, char **argv, attrlist **attributes)
{
extern char *optarg;
char *char_p;
int opt;
int flag;
flag = 0;
while ((opt = getopt(argc, argv, "r:aldefghsux:n:w:N")) != EOF) {
switch (opt) {
case 'r':
if (repository.type != NULL) {
(void) fprintf(stderr, gettext(
"Repository is already defined or specified.\n"));
rusage();
retval = BADSYN;
return (FAIL);
}
if (strcmp(optarg, "nis") == 0) {
repository.type = optarg;
} else if (strcmp(optarg, "ldap") == 0) {
repository.type = optarg;
} else if (strcmp(optarg, "files") == 0) {
repository.type = optarg;
} else {
(void) fprintf(stderr,
gettext("invalid repository: %s\n"),
optarg);
rusage();
retval = BADSYN;
return (FAIL);
}
break;
case 'd':
if (repository.type == NULL)
repository = __REPFILES;
if (IS_FILES(repository) == FALSE &&
IS_LDAP(repository) == FALSE) {
(void) fprintf(stderr, gettext(
"-d only applies to files "
"or ldap repository\n"));
rusage();
retval = BADSYN;
return (FAIL);
}
if (ckuid() != SUCCESS) {
retval = NOPERM;
return (FAIL);
}
if (flag & (LFLAG|SAFLAG|DFLAG|XFLAG|UFLAG)) {
rusage();
retval = BADOPT;
return (FAIL);
}
flag |= DFLAG;
attrlist_add(attributes, ATTR_PASSWD, NULL);
break;
case 'N':
if (repository.type == NULL)
repository = __REPFILES;
if (IS_FILES(repository) == FALSE &&
IS_LDAP(repository) == FALSE) {
(void) fprintf(stderr, gettext(
"-N only applies to files or ldap "
"repository\n"));
rusage();
retval = BADOPT;
return (FAIL);
}
if ((IS_FILES(repository) || IS_LDAP(repository)) &&
((retval = ckuid()) != SUCCESS))
return (FAIL);
if (flag & (MUTEXFLAG|NONAGEFLAG)) {
rusage();
retval = BADOPT;
return (FAIL);
}
flag |= XFLAG;
attrlist_add(attributes, ATTR_NOLOGIN_ACCOUNT, NULL);
break;
case 'l':
if (repository.type == NULL)
repository = __REPFILES;
if (IS_FILES(repository) == FALSE &&
IS_LDAP(repository) == FALSE) {
(void) fprintf(stderr, gettext(
"-l only applies to files or ldap "
"repository\n"));
rusage();
retval = BADOPT;
return (FAIL);
}
if ((IS_FILES(repository) || IS_LDAP(repository)) &&
((retval = ckuid()) != SUCCESS))
return (FAIL);
if (flag & (MUTEXFLAG|NONAGEFLAG)) {
rusage();
retval = BADOPT;
return (FAIL);
}
flag |= LFLAG;
attrlist_add(attributes, ATTR_LOCK_ACCOUNT, NULL);
break;
case 'u':
if (repository.type == NULL)
repository = __REPFILES;
if (IS_FILES(repository) == FALSE &&
IS_LDAP(repository) == FALSE) {
(void) fprintf(stderr, gettext(
"-u only applies to files or ldap "
"repository\n"));
rusage();
retval = BADOPT;
return (FAIL);
}
if ((IS_FILES(repository) || IS_LDAP(repository)) &&
((retval = ckuid()) != SUCCESS))
return (FAIL);
if (flag & (MUTEXFLAG|NONAGEFLAG)) {
rusage();
retval = BADOPT;
return (FAIL);
}
flag |= UFLAG;
attrlist_add(attributes, ATTR_UNLOCK_ACCOUNT, NULL);
attrlist_add(attributes, ATTR_RST_FAILED_LOGINS, NULL);
break;
case 'x':
if (repository.type == NULL)
repository = __REPFILES;
if (IS_FILES(repository) == FALSE &&
IS_LDAP(repository) == FALSE) {
(void) fprintf(stderr, gettext(
"-x only applies to files or ldap "
"repository\n"));
rusage();
retval = BADSYN;
return (FAIL);
}
if ((IS_FILES(repository) || IS_LDAP(repository)) &&
(ckuid() != SUCCESS)) {
retval = NOPERM;
return (FAIL);
}
if (flag & (SAFLAG|MFLAG|NONAGEFLAG)) {
retval = BADOPT;
return (FAIL);
}
flag |= MFLAG;
if ((int)strlen(optarg) <= 0 ||
(maxdate = strtol(optarg, &char_p, 10)) < -1 ||
*char_p != '\0') {
(void) fprintf(stderr, "%s: %s -x\n",
prognamep, gettext(MSG_NV));
retval = BADSYN;
return (FAIL);
}
attrlist_add(attributes, ATTR_MAX, optarg);
break;
case 'n':
if (repository.type == NULL)
repository = __REPFILES;
if (IS_FILES(repository) == FALSE &&
IS_LDAP(repository) == FALSE) {
(void) fprintf(stderr, gettext(
"-n only applies to files or ldap "
"repository\n"));
rusage();
retval = BADSYN;
return (FAIL);
}
if ((IS_FILES(repository) || IS_LDAP(repository)) &&
((retval = ckuid()) != SUCCESS))
return (FAIL);
if (flag & (SAFLAG|NFLAG|NONAGEFLAG)) {
retval = BADOPT;
return (FAIL);
}
flag |= NFLAG;
if ((int)strlen(optarg) <= 0 ||
(strtol(optarg, &char_p, 10)) < 0 ||
*char_p != '\0') {
(void) fprintf(stderr, "%s: %s -n\n",
prognamep, gettext(MSG_NV));
retval = BADSYN;
return (FAIL);
}
attrlist_add(attributes, ATTR_MIN, optarg);
break;
case 'w':
if (repository.type == NULL)
repository = __REPFILES;
if (IS_FILES(repository) == FALSE &&
IS_LDAP(repository) == FALSE) {
(void) fprintf(stderr, gettext(
"-w only applies to files or ldap "
"repository\n"));
rusage();
retval = BADSYN;
return (FAIL);
}
if ((IS_FILES(repository) || IS_LDAP(repository)) &&
(ckuid() != SUCCESS)) {
retval = NOPERM;
return (FAIL);
}
if (flag & (SAFLAG|WFLAG|NONAGEFLAG)) {
retval = BADOPT;
return (FAIL);
}
flag |= WFLAG;
if ((int)strlen(optarg) <= 0 ||
(strtol(optarg, &char_p, 10)) < 0 ||
*char_p != '\0') {
(void) fprintf(stderr, "%s: %s -w\n",
prognamep, gettext(MSG_NV));
retval = BADSYN;
return (FAIL);
}
attrlist_add(attributes, ATTR_WARN, optarg);
break;
case 's':
if (repository.type == NULL)
repository = __REPFILES;
if (IS_FILES(repository) == FALSE &&
IS_LDAP(repository) == FALSE) {
(void) fprintf(stderr, gettext(
"-s only applies to files or ldap "
"repository\n"));
rusage();
retval = BADSYN;
return (FAIL);
}
if ((IS_FILES(repository) || IS_LDAP(repository)) &&
((retval = ckuid()) != SUCCESS))
return (FAIL);
if (flag && (flag != AFLAG)) {
retval = BADOPT;
return (FAIL);
}
flag |= SFLAG;
break;
case 'a':
if (repository.type == NULL)
repository = __REPFILES;
if (IS_FILES(repository) == FALSE &&
IS_LDAP(repository) == FALSE) {
(void) fprintf(stderr, gettext(
"-a only applies to files or ldap "
"repository\n"));
rusage();
retval = BADSYN;
return (FAIL);
}
if ((IS_FILES(repository) || IS_LDAP(repository)) &&
((retval = ckuid()) != SUCCESS))
return (FAIL);
if (flag && (flag != SFLAG)) {
retval = BADOPT;
return (FAIL);
}
flag |= AFLAG;
break;
case 'f':
if (repository.type == NULL)
repository = __REPFILES;
if (IS_FILES(repository) == FALSE &&
IS_LDAP(repository) == FALSE) {
(void) fprintf(stderr, gettext(
"-f only applies to files or ldap "
"repository\n"));
rusage();
retval = BADSYN;
return (FAIL);
}
if ((IS_FILES(repository) || IS_LDAP(repository)) &&
((retval = ckuid()) != SUCCESS))
return (FAIL);
if (flag & (SAFLAG|FFLAG|NONAGEFLAG)) {
retval = BADOPT;
return (FAIL);
}
flag |= FFLAG;
attrlist_add(attributes, ATTR_EXPIRE_PASSWORD, NULL);
break;
case 'e':
if (repository.type == NULL)
repository = __REPFILES;
if (flag & (EFLAG|SAFLAG|AGEFLAG)) {
retval = BADOPT;
return (FAIL);
}
flag |= EFLAG;
break;
case 'g':
if (repository.type == NULL)
repository = __REPFILES;
if (IS_FILES(repository) && (ckuid() != SUCCESS)) {
retval = NOPERM;
return (FAIL);
}
if (flag & (GFLAG|SAFLAG|AGEFLAG)) {
retval = BADOPT;
return (FAIL);
}
flag |= GFLAG;
break;
case 'h':
if (repository.type == NULL)
repository = __REPFILES;
if (IS_FILES(repository) && (ckuid() != SUCCESS)) {
retval = NOPERM;
return (FAIL);
}
if (IS_NIS(repository)) {
(void) fprintf(stderr, "%s\n",
gettext(MSG_NIS_HOMEDIR));
retval = BADSYN;
return (FAIL);
}
if (flag & (HFLAG|SAFLAG|AGEFLAG)) {
retval = BADOPT;
return (FAIL);
}
flag |= HFLAG;
break;
case '?':
rusage();
retval = BADOPT;
return (FAIL);
}
}
argc -= optind;
if (argc > 1) {
rusage();
retval = BADSYN;
return (FAIL);
}
attrlist_reorder(attributes);
if (!flag || (flag == SFLAG))
return (flag);
if (flag == AFLAG) {
rusage();
retval = BADSYN;
return (FAIL);
}
if (flag != SAFLAG && argc < 1) {
if (!(flag & NONAGEFLAG)) {
rusage();
retval = BADSYN;
return (FAIL);
}
}
if (flag == SAFLAG && argc >= 1) {
rusage();
retval = BADSYN;
return (FAIL);
}
if ((maxdate == -1) && (flag & NFLAG)) {
(void) fprintf(stderr, "%s: %s -n\n",
prognamep, gettext(MSG_NV));
retval = BADOPT;
return (FAIL);
}
return (flag);
}
static int
ckuid(void)
{
if (uid != 0) {
return (retval = NOPERM);
}
return (SUCCESS);
}
int
get_attr(char *username, pwu_repository_t *repository, attrlist **attributes)
{
int res;
attrlist_add(attributes, ATTR_PASSWD, NULL);
attrlist_add(attributes, ATTR_LSTCHG, "0");
attrlist_add(attributes, ATTR_MIN, "0");
attrlist_add(attributes, ATTR_MAX, "0");
attrlist_add(attributes, ATTR_WARN, "0");
res = __get_authtoken_attr(username, repository, *attributes);
if (res == PWU_SUCCESS) {
retval = SUCCESS;
return (PWU_SUCCESS);
}
if (res == PWU_NOT_FOUND)
(void) fprintf(stderr, gettext(MSG_UNKNOWN), prognamep,
username);
retval = NOPERM;
passwd_exit(retval);
}
void
display_attr(char *usrname, attrlist *attributes)
{
char *status = NULL;
char *passwd;
long lstchg;
int min = 0, max = 0, warn = 0;
while (attributes) {
switch (attributes->type) {
case ATTR_PASSWD:
passwd = attributes->data.val_s;
if (passwd == NULL || *passwd == '\0')
status = "NP ";
else if (strncmp(passwd, LOCKSTRING,
sizeof (LOCKSTRING)-1) == 0)
status = "LK ";
else if (strncmp(passwd, NOLOGINSTRING,
sizeof (NOLOGINSTRING)-1) == 0)
status = "NL ";
else if ((strlen(passwd) == 13 && passwd[0] != '$') ||
passwd[0] == '$')
status = "PS ";
else
status = "UN ";
break;
case ATTR_LSTCHG:
lstchg = attributes->data.val_i * DAY;
break;
case ATTR_MIN:
min = attributes->data.val_i;
break;
case ATTR_MAX:
max = attributes->data.val_i;
break;
case ATTR_WARN:
warn = attributes->data.val_i;
break;
default:
break;
}
attributes = attributes->next;
}
(void) fprintf(stdout, "%-8s ", usrname);
if (status)
(void) fprintf(stdout, "%s ", status);
if (max != -1) {
if (lstchg == 0) {
(void) fprintf(stdout, "00/00/00 ");
} else {
struct tm *tmp;
tmp = gmtime(&lstchg);
(void) fprintf(stdout, "%.2d/%.2d/%.2d ",
tmp->tm_mon + 1,
tmp->tm_mday,
tmp->tm_year % 100);
}
(void) fprintf(stdout, (min >= 0) ? "%4d " : " ", min);
(void) fprintf(stdout, "%4d ", max);
(void) fprintf(stdout, (warn > 0) ? "%4d " : " ", warn);
}
(void) fprintf(stdout, "\n");
}
void
free_attr(attrlist *attributes)
{
while (attributes) {
if (attributes->type == ATTR_PASSWD)
free(attributes->data.val_s);
attributes = attributes->next;
}
}
int
get_namelist_files(char ***namelist_p, int *num_user)
{
FILE *pwfp;
struct passwd *pwd;
int max_user;
int nuser;
char **nl;
nuser = 0;
errno = 0;
pwd = NULL;
if ((pwfp = fopen(PASSWD, "r")) == NULL)
return (NOPERM);
max_user = 1;
while ((pwd = fgetpwent(pwfp)) != NULL)
max_user++;
rewind(pwfp);
nl = (char **)calloc(max_user, (sizeof (char *)));
if (nl == NULL) {
(void) fclose(pwfp);
return (FMERR);
}
while ((pwd = fgetpwent(pwfp)) != NULL) {
if ((nl[nuser] = strdup(pwd->pw_name)) == NULL) {
(void) fclose(pwfp);
return (FMERR);
}
nuser++;
}
nl[nuser] = NULL;
*num_user = nuser;
*namelist_p = nl;
(void) fclose(pwfp);
return (SUCCESS);
}
#define NSS_LDAP_ONLY "ldap"
extern int str2spwd(const char *, int, void *, char *, int);
static DEFINE_NSS_DB_ROOT(db_root);
static DEFINE_NSS_GETENT(context);
static char *local_config;
static void
_lc_nss_initf_shadow(nss_db_params_t *p)
{
p->name = NSS_DBNAM_SHADOW;
p->config_name = NSS_DBNAM_PASSWD;
p->default_config = local_config;
p->flags = NSS_USE_DEFAULT_CONFIG;
}
static void
_lc_setspent(void)
{
nss_setent(&db_root, _lc_nss_initf_shadow, &context);
}
static void
_lc_endspent(void)
{
nss_endent(&db_root, _lc_nss_initf_shadow, &context);
nss_delete(&db_root);
}
static struct spwd *
_lc_getspent_r(struct spwd *result, char *buffer, int buflen)
{
nss_XbyY_args_t arg;
char *nam;
do {
NSS_XbyY_INIT(&arg, result, buffer, buflen, str2spwd);
(void) nss_getent(&db_root, _lc_nss_initf_shadow, &context,
&arg);
} while (arg.returnval != 0 &&
(nam = ((struct spwd *)arg.returnval)->sp_namp) != 0 &&
(*nam == '+' || *nam == '-'));
return (struct spwd *)NSS_XbyY_FINI(&arg);
}
static nss_XbyY_buf_t *buffer;
static struct spwd *
_lc_getspent(void)
{
nss_XbyY_buf_t *b;
b = NSS_XbyY_ALLOC(&buffer, sizeof (struct spwd), NSS_BUFLEN_SHADOW);
return (b == 0 ? 0 : _lc_getspent_r(b->result, b->buffer, b->buflen));
}
int
get_namelist_local(char ***namelist_p, int *num_user)
{
int nuser = 0;
int alloced = 100;
char **nl;
struct spwd *p;
if ((nl = calloc(alloced, sizeof (*nl))) == NULL)
return (FMERR);
(void) _lc_setspent();
while ((p = _lc_getspent()) != NULL) {
if ((nl[nuser] = strdup(p->sp_namp)) == NULL) {
_lc_endspent();
return (FMERR);
}
if (++nuser == alloced) {
alloced += 100;
nl = realloc(nl, alloced * (sizeof (*nl)));
if (nl == NULL) {
_lc_endspent();
return (FMERR);
}
}
}
(void) _lc_endspent();
nl[nuser] = NULL;
*namelist_p = nl;
*num_user = nuser;
return (SUCCESS);
}
int
get_namelist(pwu_repository_t repository, char ***namelist, int *num_user)
{
if (IS_LDAP(repository)) {
local_config = NSS_LDAP_ONLY;
return (get_namelist_local(namelist, num_user));
} else if (IS_FILES(repository))
return (get_namelist_files(namelist, num_user));
rusage();
return (BADSYN);
}
void
passwd_exit(int retcode)
{
if (pamh)
(void) pam_end(pamh, pam_retval);
switch (retcode) {
case SUCCESS:
break;
case NOPERM:
(void) fprintf(stderr, "%s\n", gettext(MSG_NP));
break;
case BADOPT:
(void) fprintf(stderr, "%s\n", gettext(MSG_BS));
break;
case FMERR:
(void) fprintf(stderr, "%s\n", gettext(MSG_FE));
break;
case FATAL:
(void) fprintf(stderr, "%s\n", gettext(MSG_FF));
break;
case FBUSY:
(void) fprintf(stderr, "%s\n", gettext(MSG_FB));
break;
case BADSYN:
(void) fprintf(stderr, "%s\n", gettext(MSG_NV));
break;
case BADAGE:
(void) fprintf(stderr, "%s\n", gettext(MSG_AD));
break;
case NOMEM:
(void) fprintf(stderr, "%s\n", gettext(MSG_NM));
break;
default:
(void) fprintf(stderr, "%s\n", gettext(MSG_NP));
retcode = NOPERM;
break;
}
if (event != NULL) {
struct passwd *pass;
if ((pass = getpwnam(usrname)) == NULL) {
event->adt_passwd.username = usrname;
} else if (pass->pw_uid != uid) {
event->adt_passwd.uid = pass->pw_uid;
event->adt_passwd.username = pass->pw_name;
}
if (adt_put_event(event,
retcode == SUCCESS ? ADT_SUCCESS : ADT_FAILURE,
retcode == SUCCESS ? ADT_SUCCESS : ADT_FAIL_PAM +
pam_retval) != 0) {
adt_free_event(event);
(void) adt_end_session(ah);
perror("adt_put_event");
exit(retcode);
}
adt_free_event(event);
}
(void) adt_end_session(ah);
exit(retcode);
}
static int
passwd_conv(int num_msg, const struct pam_message **msg,
struct pam_response **response, void *appdata_ptr)
{
const struct pam_message *m;
struct pam_response *r;
char *temp;
int k, i;
if (num_msg <= 0)
return (PAM_CONV_ERR);
*response = (struct pam_response *)calloc(num_msg,
sizeof (struct pam_response));
if (*response == NULL)
return (PAM_BUF_ERR);
k = num_msg;
m = *msg;
r = *response;
while (k--) {
switch (m->msg_style) {
case PAM_PROMPT_ECHO_OFF:
temp = getpassphrase(m->msg);
if (temp != NULL) {
r->resp = strdup(temp);
(void) memset(temp, 0, strlen(temp));
if (r->resp == NULL) {
r = *response;
for (i = 0; i < num_msg; i++, r++) {
if (r->resp)
free(r->resp);
}
free(*response);
*response = NULL;
return (PAM_BUF_ERR);
}
}
m++;
r++;
break;
case PAM_PROMPT_ECHO_ON:
if (m->msg != NULL) {
(void) fputs(m->msg, stdout);
}
r->resp = (char *)calloc(PAM_MAX_RESP_SIZE,
sizeof (char));
if (r->resp == NULL) {
r = *response;
for (i = 0; i < num_msg; i++, r++) {
if (r->resp)
free(r->resp);
}
free(*response);
*response = NULL;
return (PAM_BUF_ERR);
}
if (fgets(r->resp, PAM_MAX_RESP_SIZE-1, stdin)) {
int len = strlen(r->resp);
if (r->resp[len-1] == '\n')
r->resp[len-1] = '\0';
}
m++;
r++;
break;
case PAM_ERROR_MSG:
if (m->msg != NULL) {
(void) fputs(m->msg, stderr);
(void) fputs("\n", stderr);
}
m++;
r++;
break;
case PAM_TEXT_INFO:
if (m->msg != NULL) {
(void) fputs(m->msg, stdout);
(void) fputs("\n", stdout);
}
m++;
r++;
break;
default:
break;
}
}
return (PAM_SUCCESS);
}
void
attrlist_add(attrlist **l, attrtype type, char *val)
{
attrlist **w;
for (w = l; *w != NULL; w = &(*w)->next)
;
if ((*w = malloc(sizeof (**w))) == NULL)
passwd_exit(NOMEM);
(*w)->type = type;
(*w)->next = NULL;
switch (type) {
case ATTR_MIN:
case ATTR_WARN:
case ATTR_MAX:
(*w)->data.val_i = atoi(val);
break;
default:
(*w)->data.val_s = val;
break;
}
}
static void
attrlist_reorder(attrlist **l)
{
attrlist **w;
attrlist *exp = NULL;
attrlist *max = NULL;
if (*l == NULL || (*l)->next == NULL)
return;
w = l;
while (*w != NULL) {
if ((*w)->type == ATTR_EXPIRE_PASSWORD) {
exp = *w;
*w = (*w)->next;
} else if ((*w)->type == ATTR_MAX) {
max = *w;
*w = (*w)->next;
} else
w = &(*w)->next;
}
if (max) {
*w = max;
w = &max->next;
}
if (exp) {
*w = exp;
w = &exp->next;
}
*w = NULL;
}
void
rusage(void)
{
#define MSG(a) (void) fprintf(stderr, gettext((a)));
MSG("usage:\n");
MSG("\tpasswd [-r files | -r nis | -r ldap] [name]\n");
MSG("\tpasswd [-r files] [-egh] [name]\n");
MSG("\tpasswd [-r files] -sa\n");
MSG("\tpasswd [-r files] -s [name]\n");
MSG("\tpasswd [-r files] [-d|-l|-N|-u] [-f] [-n min] [-w warn] "
"[-x max] name\n");
MSG("\tpasswd -r nis [-eg] [name]\n");
MSG("\t\t[-x max] name\n");
MSG("\tpasswd -r ldap [-egh] [name]\n");
MSG("\tpasswd -r ldap -sa\n");
MSG("\tpasswd -r ldap -s [name]\n");
MSG("\tpasswd -r ldap [-l|-N|-u] [-f] [-n min] [-w warn] "
"[-x max] name\n");
#undef MSG
}