passmgmt.c revision 3bf5ae9eedb977fad5c8a4029f296a9ec010c06e
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdio.h>
#include <shadow.h>
#include <pwd.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <time.h>
#include <unistd.h>
#include <stdlib.h>
#include <locale.h>
#include <fcntl.h>
#include <secdb.h>
#include <user_attr.h>
#define H_MASK 010
#define U_MASK 020
#define G_MASK 040
#define S_MASK 0100
#define O_MASK 0200
#define A_MASK 0400
#define D_MASK 01000
#define F_MASK 02000
#define E_MASK 04000
#define UATTR_MASK 010000
/* flags for info_mask */
#define BAD_ENT_MESSAGE "%s: Bad entry found in /etc/passwd. Run pwconv.\n"
typedef struct kvopts {
const char option;
const char *key;
char *newvalue;
} kvopts_t;
/* mapping of extensible keywords and options */
{ 'A', USERATTR_AUTHS_KW },
{ 'P', USERATTR_PROFILES_KW },
{ 'R', USERATTR_ROLES_KW },
{ 'T', USERATTR_TYPE_KW },
{ '\0', USERATTR_DEFAULTPROJ_KW },
{ '\0', USERATTR_LIMPRIV_KW },
{ '\0', USERATTR_DFLTPRIV_KW },
{ '\0', USERATTR_LOCK_AFTER_RETRIES_KW },
{ '\0', USERATTR_LABELVIEW },
{ '\0', USERATTR_CLEARANCE },
{ '\0', USERATTR_MINLABEL },
{ '\0', USERATTR_IDLECMD_KW },
{ '\0', USERATTR_IDLETIME_KW },
};
char *msg; /* pointer to error message */
#define OUSERATTR_FILENAME "/etc/ouser_attr"
#define USERATTR_TEMP "/etc/uatmp"
struct uid_blk {
};
/*
* Declare all functions that do not return integers. This is here
* to get rid of some lint messages
*/
bad_perm(void),
/*
* The uid_blk structure is used in the search for the default
* uid. Each uid_blk represent a range of uid(s) that are currently
* used on the system.
*/
#ifndef att
/*
* getspnan routine that ONLY looks at the local shadow file
*/
struct spwd *
local_getspnam(char *name)
{
return (NULL);
break;
}
return (sp);
}
#endif
static void
{
int i, j;
char *key;
char *val;
/*
* Avoid trivial entries. Those with no attributes or with
* only "type=normal". This retains backward compatibility.
*/
return;
break;
continue;
j++;
}
if (j == 0)
return;
break;
continue;
if (j > 0)
(void) fprintf(f, KV_DELIMITER);
j++;
}
(void) fprintf(f, "\n");
}
static void
int i;
char *key;
int avail = -1;
avail = i;
continue;
return;
}
}
if (avail == -1)
}
}
static void
char *roleptr;
char *templist;
char *temprole;
int length;
while (temprole) {
length++;
break;
} else {
}
}
}
char *prognamp; /* program name */
extern int errno;
extern int getdate_err;
int
{
int c, i;
int end_of_file = 0;
int error;
long date = 0;
int NIS_entry_seen; /* NIS scanning flag */
/*
* NIS start pos, really pointer to first entry AFTER first
* NIS-referant entry
*/
long NIS_pos;
long cur_pos; /* Current pos, used with nis-pos above */
#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
#define TEXT_DOMAIN "SYS_TEST"
#endif
(void) textdomain(TEXT_DOMAIN);
tzset();
/* Get program name */
/* Check identity */
if (geteuid() != 0)
bad_perm();
/* Lock the password file(s) */
if (lckpwdf() != 0)
no_lock();
/* initialize the two structures */
/* parse the command line */
"ml:c:h:u:g:s:f:e:k:A:P:R:T:oadK:")) != -1) {
switch (c) {
case 'm':
/* Modify */
bad_usage("Invalid combination of options");
break;
case 'l' :
/* Change logname */
bad_usage("Invalid combination of options");
bad_arg("Invalid argument to option -l");
break;
case 'f' :
/* set inactive */
bad_usage("Invalid combination of options");
(*char_p != '\0') ||
bad_arg("Invalid argument to option -f");
break;
case 'e' :
/* set expire date */
bad_usage("Invalid combination of options");
else {
msg = "Invalid argument to option -e";
}
msg = "Invalid argument to option -e";
}
msg = "Invalid argument to option -e";
}
}
break;
case 'c' :
/* The comment */
bad_usage("Invalid combination of options");
bad_arg("Invalid argument to option -c");
break;
case 'h' :
/* The home directory */
bad_usage("Invalid combination of options");
bad_arg("Invalid argument to option -h");
break;
case 'u' :
/* The uid */
bad_usage("Invalid combination of options");
if ((*char_p != '\0') ||
bad_arg("Invalid argument to option -u");
break;
case 'g' :
/* The gid */
bad_usage("Invalid combination of options");
bad_arg("Invalid argument to option -g");
break;
case 's' :
/* The shell */
bad_usage("Invalid combination of options");
bad_arg("Invalid argument to option -s");
break;
case 'o' :
/* Override unique uid */
bad_usage("Invalid combination of options");
break;
case 'a' :
/* Add */
bad_usage("Invalid combination of options");
break;
case 'd' :
/* Delete */
bad_usage("Invalid combination of options");
break;
case 'K':
bad_usage("Invalid combination of options");
bad_usage("Missing value in -K option");
*char_p++ = '\0';
for (i = 0; i < UA_KEYS; i++) {
char_p);
break;
}
}
if (i == UA_KEYS)
bad_usage("bad key");
optn_mask |= UATTR_MASK;
break;
case '?' :
bad_usage("");
break;
default :
/* Extended User Attributes */
{
int j;
for (j = 0; j < UA_KEYS; j++) {
bad_usage("Invalid combination"
" of options");
optn_mask |= UATTR_MASK;
break;
}
}
break;
}
}
}
/* check command syntax for the following errors */
/* too few or too many arguments */
/* no -a -m or -d option */
/* -o without -u */
/* -m with no other option */
!(optn_mask &
E_MASK|UATTR_MASK))))
bad_usage("Invalid command syntax");
/* null string argument or bad characters ? */
bad_arg("Invalid name");
/*
* if we are adding a new user or modifying an existing user
* (not the logname), then copy logname into the two data
* structures
*/
}
/* Put in directory if we are adding and we need a default */
file_error();
}
/* Check the number of password files we are touching */
info_mask |= BOTH_FILES;
info_mask |= UATTR_FILE;
/* Open the temporary file(s) with appropriate permission mask */
/* and the appropriate owner */
file_error();
if (fd_ptemp == -1) {
msg = "%s: warning: cannot unlink %s\n";
PASSTEMP);
}
if (fd_ptemp == -1) {
file_error();
}
} else
file_error();
}
file_error();
if (error == 0)
if (error != 0) {
msg = "%s: warning: cannot unlink %s\n";
PASSTEMP);
}
file_error();
}
if (info_mask & BOTH_FILES) {
rid_tmpf();
file_error();
}
if (fd_stemp == -1) {
msg = "%s: warning: cannot unlink %s\n";
}
if (fd_stemp == -1) {
rid_tmpf();
file_error();
}
} else {
rid_tmpf();
file_error();
}
}
rid_tmpf();
file_error();
}
if (error == 0)
if (error != 0) {
rid_tmpf();
file_error();
}
}
if (info_mask & UATTR_FILE) {
rid_tmpf();
file_error();
}
if (fd_uatemp == -1) {
if (unlink(USERATTR_TEMP)) {
msg = "%s: warning: cannot unlink %s\n";
}
if (fd_uatemp == -1) {
rid_tmpf();
file_error();
}
} else {
rid_tmpf();
file_error();
}
}
rid_tmpf();
file_error();
}
if (error == 0)
if (error != 0) {
rid_tmpf();
file_error();
}
}
/* Default uid needed ? */
/* mark it in the information mask */
/* create the head of the uid number list */
rid_tmpf();
file_error();
}
}
/*
* This next section is modified to allow for NIS passwd file
* conventions. In the case where a password entry was being
* added to the password file, the original AT&T code read
* the entire password file in, noted any information needed, and
* copied the entries to a temporary file. Then the new entry
* was added to the temporary file, and the temporary file was
* moved to be the real password file.
*
* The problem is, that with NIS compatability, we want to add new
* entries BEFORE the first NIS-referrant entry, so as not to have
* any surprises. To accomplish this without extensively modifying
* the logic of the code below, as soon as a NIS-referrant entry is
* found we stop copying entries to the TEMP file and instead we
* remember
* the first NIS entry and where we found it, scan the rest of the
* password file without copying entries, then write the new entry, copy
* the stored password entry, then copy the rest of the password file.
*/
error = 0;
rid_tmpf();
bad_news();
else
file_error();
}
NIS_entry_seen = 0;
cur_pos = 0;
/* The while loop for reading PASSWD entries */
while (!end_of_file) {
/* A real error - report it and exit */
rid_tmpf();
bad_pasf();
}
else
break;
}
if (!NIS_entry_seen)
else
info_mask &= ~WRITE_P_ENTRY;
/*
* Set up the uid usage blocks to find the first
* available uid above UID_MIN, if needed
*/
if (info_mask & NEED_DEF_UID)
/* Check for unique UID */
rid_tmpf(); /* get rid of temp files */
bad_uid();
}
/* Check for unique new logname */
rid_tmpf();
#ifdef att
#else
#endif
bad_pasf();
else
bad_name("logname already exists");
}
/* no good if we want to add an existing logname */
rid_tmpf();
#ifdef att
#else
if (!local_getspnam(lognamp))
#endif
bad_pasf();
else
bad_name("name already exists");
}
/* remember we found it */
/* Do not write it out on the fly */
info_mask &= ~WRITE_P_ENTRY;
#ifdef att
#else
if (!local_getspnam(lognamp))
#endif
{
rid_tmpf();
bad_pasf();
}
}
}
}
if (!NIS_entry_seen) {
char *p;
if (p != NULL) {
/*
* Found first NIS entry.
* so remember it.
*/
NIS_entry_seen = 1;
info_mask &= ~WRITE_P_ENTRY;
}
else
}
}
if (info_mask & WRITE_P_ENTRY) {
rid_tmpf();
file_error();
}
}
} /* end-of-while-loop */
if (error >= 1) {
}
/* Cannot find the target entry and we are deleting or modifying */
rid_tmpf();
#ifdef att
#else
#endif
bad_pasf();
else
bad_name("name does not exist");
}
/* First available uid above UID_MIN is ... */
if (info_mask & NEED_DEF_UID)
/* Write out the added entry now */
rid_tmpf();
file_error();
}
/*
* Now put out the rest of the password file, if needed.
*/
if (NIS_entry_seen) {
int n;
char buf[1024];
rid_tmpf();
file_error();
}
!= n) {
rid_tmpf();
file_error();
}
}
}
}
/* flush and sync the file before closing it */
file_error();
/* Now we are done with PASSWD */
/* Do this if we are touching both password files */
if (info_mask & BOTH_FILES) {
/* The while loop for reading SHADOW entries */
end_of_file = 0;
errno = 0;
error = 0;
NIS_entry_seen = 0;
cur_pos = 0;
rid_tmpf();
file_error();
}
while (!end_of_file) {
rid_tmpf();
bad_pasf();
}
else
break;
}
if (!NIS_entry_seen)
else
info_mask &= ~WRITE_S_ENTRY;
/*
* See if the new logname already exist in the
* shadow passwd file
*/
rid_tmpf();
bad_pasf();
}
/* password file inconsistent */
rid_tmpf();
bad_pasf();
}
}
info_mask &= ~WRITE_S_ENTRY;
}
if (!NIS_entry_seen) {
char *p;
if (p != NULL) {
/*
* Found first NIS entry.
* so remember it.
*/
NIS_entry_seen = 1;
info_mask &= ~WRITE_S_ENTRY;
}
else
}
}
if (info_mask & WRITE_S_ENTRY) {
rid_tmpf();
file_error();
}
}
} /* end-of-while-loop */
if (error >= 1) {
}
/*
* If we cannot find the entry and we are deleting or
* modifying
*/
rid_tmpf();
bad_pasf();
}
rid_tmpf();
file_error();
}
/*
* Now put out the rest of the shadow file, if needed.
*/
if (NIS_entry_seen) {
}
}
/* flush and sync the file before closing it */
file_error();
/* Done with SHADOW */
} /* End of if info_mask */
if (info_mask & UATTR_FILE) {
/* The while loop for reading USER_ATTR entries */
end_of_file = 0;
errno = 0;
error = 0;
NIS_entry_seen = 0;
cur_pos = 0;
rid_tmpf();
file_error();
}
while (!end_of_file) {
rid_tmpf();
bad_uattr();
}
else
break;
}
/*
* If this is a comment, write it back as it
* is.
*/
else
/*
* This is a commented user_attr entry;
* reformat it, and write it back.
*/
continue;
}
if (!NIS_entry_seen)
else
info_mask &= ~WRITE_S_ENTRY;
/*
* See if the new logname already exist in the
* user_attr file
*/
rid_tmpf();
bad_pasf();
}
/* password file inconsistent */
rid_tmpf();
bad_pasf();
}
int j;
char *value;
for (j = 0; j < UA_KEYS; j++) {
continue;
value =
continue;
value);
}
ua_ptr1p = &userattr_st;
}
info_mask &= ~WRITE_S_ENTRY;
char *rolelist;
if (rolelist) {
}
}
if (info_mask & WRITE_S_ENTRY) {
}
} /* end-of-while-loop */
if (error >= 1) {
}
/*
* Add entry in user_attr if masks is UATTR_MASK
* We don't need to do anything for L_MASK if there's
* no user_attr entry for the user being modified.
*/
}
/* flush and sync the file before closing it */
file_error();
/* Done with USERATTR */
} /* End of if info_mask */
/* ignore all signals */
for (i = 1; i < NSIG; i++)
errno = 0; /* For correcting sigset to SIGKILL */
file_error();
file_error();
bad_news();
file_error();
}
if (info_mask & BOTH_FILES) {
if (rec_pwd())
bad_news();
else
file_error();
}
if (rec_pwd())
bad_news();
else
file_error();
}
bad_news();
if (rec_pwd())
bad_news();
else
file_error();
}
}
if (info_mask & UATTR_FILE) {
if (unlink(OUSERATTR_FILENAME) &&
access(OUSERATTR_FILENAME, 0) == 0) {
if (rec_pwd())
bad_news();
else
file_error();
}
if (rec_pwd())
bad_news();
else
file_error();
}
bad_news();
if (rec_pwd())
bad_news();
else
file_error();
}
}
ulckpwdf();
/*
* Return 0 status, indicating success
*/
return (0);
} /* end of main */
/* Try to recover the old password file */
int
rec_pwd(void)
{
return (-1);
return (0);
}
/* combine two uid_blk's */
void
{
}
/* add a new uid_blk */
void
{
rid_tmpf();
file_error();
}
}
/*
* Here we are using a linked list of uid_blk to keep track of all
* the used uids. Each uid_blk represents a range of used uid,
* with low represents the low inclusive end and high represents
* the high inclusive end. In the beginning, we initialize a linked
* list of one uid_blk with low = high = (UID_MIN-1). This was
* done in main().
* Each time we read in another used uid, we add it onto the linked
* list by either making a new uid_blk, decrementing the low of
* an existing uid_blk, incrementing the high of an existing
* uid_blk, or combining two existing uid_blks. After we finished
* building this linked list, the first available uid above or
* equal to UID_MIN is the high of the first uid_blk in the linked
* list + 1.
*/
/* add_uid() adds uid to the link list of used uids */
void
{
/* Only keep track of the ones above UID_MIN */
}
}
}
}
}
} /* if uid_p->link */
else {
} else {
}
} /* else */
} /* while uid_p */
} /* if uid */
}
void
bad_perm(void)
{
exit(1);
}
void
{
%s -a [-c comment] [-h homedir] [-u uid [-o]] [-g gid] \n\
[-s shell] [-f inactive] [-e expire] name\n\
%s -m -c comment | -h homedir | -u uid [-o] | -g gid |\n\
-s shell | -f inactive | -e expire | -l logname name\n\
ulckpwdf();
exit(2);
}
void
bad_arg(char *s)
{
ulckpwdf();
exit(3);
}
void
bad_name(char *s)
{
ulckpwdf();
exit(9);
}
void
bad_uid(void)
{
ulckpwdf();
exit(4);
}
void
bad_pasf(void)
{
msg = "%s: Inconsistent password files\n";
ulckpwdf();
exit(5);
}
void
bad_uattr(void)
{
msg = "%s: Bad user_attr database\n";
ulckpwdf();
exit(5);
}
void
file_error(void)
{
msg = "%s: Unexpected failure. Password files unchanged\n";
ulckpwdf();
exit(6);
}
void
bad_news(void)
{
msg = "%s: Unexpected failure. Password file(s) missing\n";
ulckpwdf();
exit(7);
}
void
no_lock(void)
{
msg = "%s: Password file(s) busy. Try again later\n";
exit(8);
}
/* Check for the size of the whole passwd entry */
void
{
char ctp[128];
/* Ensure that the combined length of the individual */
/* fields will fit in a passwd entry. The 1 accounts for the */
/* newline and the 6 accounts for the colons (:'s) */
rid_tmpf();
bad_arg("New password entry too long");
}
}
/* Check for the size of the whole passwd entry */
void
{
char ctp[128];
/* Ensure that the combined length of the individual */
/* fields will fit in a shadow entry. The 1 accounts for the */
/* newline and the 7 accounts for the colons (:'s) */
rid_tmpf();
bad_arg("New password entry too long");
}
}
/* Get rid of the temp files */
void
rid_tmpf(void)
{
msg = "%s: warning: cannot unlink %s\n";
}
if (info_mask & BOTH_FILES) {
msg = "%s: warning: cannot unlink %s\n";
SHADTEMP);
}
}
if (info_mask & UATTR_FILE) {
if (unlink(USERATTR_TEMP)) {
msg = "%s: warning: cannot unlink %s\n";
}
}
}
void
{
int n;
char buf[1024];
rid_tmpf();
file_error();
}
rid_tmpf();
file_error();
}
}
}