OpenSolaris_b135/cmd/keyserv/chkey.c

Compare this file to the similar file:
Show the results in this format:

/*
 * 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
 * or http://www.opensolaris.org/os/licensing.
 * 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 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
/*	  All Rights Reserved  	*/

/*
 * University Copyright- Copyright (c) 1982, 1986, 1988
 * The Regents of the University of California
 * All Rights Reserved
 *
 * University Acknowledgment- Portions of this document are derived from
 * software developed by the University of California, Berkeley, and its
 * contributors.
 */


#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pwd.h>
#include <shadow.h>
#include <crypt.h>
#include <sys/types.h>
#include <unistd.h>
#include <rpc/rpc.h>
#include <rpc/key_prot.h>
#include <rpcsvc/nis.h>
#include <rpcsvc/nis_dhext.h>
#include <rpcsvc/ypclnt.h>
#include <nsswitch.h>

#define	PK_FILES	1
#define	PK_YP		2
#define	PK_LDAP		4

#define	CURMECH		mechs[mcount]
#define	DESCREDPASSLEN	sizeof (des_block)

static char	CRED_TABLE[] = "cred.org_dir";
static char	PKMAP[] = "publickey.byname";
static char	PKFILE[] = "/etc/publickey";
#define	MAXHOSTNAMELEN	256

#define	ROOTKEY_FILE		"/etc/.rootkey"
#define	ROOTKEY_FILE_BACKUP	"/etc/.rootkey.bak"
#define	MAXROOTKEY_LINE_LEN	4224	/* Good upto 16384-bit keys */
#define	MAXROOTKEY_LEN		4096

/* Should last up to 16384-bit keys */
#define	MAXPKENTLEN	8500

bool_t		makenew = TRUE;   /* Make new keys or reencrypt existing */
bool_t		specmech = FALSE; /* Specific mechs requested */
bool_t		force = FALSE;
int		dest_service = 0; /* To which nameservice do we store key(s) */

char		*program_name;

mechanism_t	**mechs = NULL;   /* List of DH mechanisms */
char		**plist = NULL;	  /* List of public key(s) */
char		**slist = NULL;	  /* List of secret key(s) */
char		**clist = NULL;   /* List of encrypted secret key(s) */
int		numspecmech = 0;  /* Number of mechanisms specified */

struct passwd	*pw = NULL;	  /* passwd entry of user */
struct spwd	*spw = NULL;	  /* shadow entry of user */

char		*netname = NULL;  /* RPC netname of user */
char		local_domain[MAXNETNAMELEN + 1];
char		*sec_domain = NULL;

char		**rpc_pws = NULL; /* List of S-RPC passwords */
int		rpc_pw_count = 0; /* Number of passwords entered by user */
char		*login_pw = NULL; /* Unencrypted login password */
char		short_login_pw[DESCREDPASSLEN + 1];
/* Short S-RPC password, which has first 8 chars of login_pw */

static int add_cred_obj(nis_object *, char *);
static void cmp_passwd();
static void encryptkeys();
static void error_msg();
static char *fgets_ignorenul();
static void getpublics();
static void getrpcpws();
static void getsecrets();
static void initkeylist(bool_t);
static void keylogin(keylen_t, algtype_t);
static void keylogin_des();
static void makenewkeys();
static int modify_cred_obj(nis_object *, char *);
static void storekeys();
static void usage();
static void write_rootkey();

extern nis_object *init_entry();
extern int get_pk_source(char *);
extern int localupdate(char *, char *, uint_t, char *);
extern int xencrypt();
extern int xencrypt_g();
extern int __gen_dhkeys();
extern int key_setnet();
extern int key_setnet_g();
extern int key_secretkey_is_set_g();
extern int __getnetnamebyuid();
extern int getdomainname();
extern int ldap_update(char *, char *, char *, char *, char *);


static void
error_msg()
{
	if (sec_domain && *sec_domain &&
	    strcasecmp(sec_domain, local_domain)) {
		fprintf(stderr,
"The system default domain '%s' is different from the Secure RPC\n\
domain %s where the key is stored. \n", local_domain, sec_domain);
		exit(1);
	}
}


static void
usage()
{
	fprintf(stderr, "usage: %s [-p] [-s ldap | nis | files] \n",
	    program_name);
	exit(1);
}


/* Encrypt secret key(s) with login_pw */
static void
encryptkeys()
{
	int	mcount, ccount = 0;

	if (mechs) {
		for (mcount = 0; CURMECH; mcount++) {
			char		*crypt = NULL;

			if (!xencrypt_g(slist[mcount], CURMECH->keylen,
			    CURMECH->algtype, short_login_pw, netname,
			    &crypt, TRUE)) {
				/* Could not crypt key */
				crypt = NULL;
			} else
				ccount++;
			clist[mcount] = crypt;
		}
	} else {
		char		*crypt = NULL;

		if (!(crypt =
		    (char *)malloc(HEXKEYBYTES + KEYCHECKSUMSIZE + 1))) {
			fprintf(stderr, "%s: Malloc failure.\n", program_name);
			exit(1);
		}

		(void) memcpy(crypt, slist[0], HEXKEYBYTES);
		(void) memcpy(crypt + HEXKEYBYTES, slist[0], KEYCHECKSUMSIZE);
		crypt[HEXKEYBYTES + KEYCHECKSUMSIZE] = 0;
		xencrypt(crypt, short_login_pw);

		clist[0] = crypt;
		ccount++;
	}

	if (!ccount) {
		fprintf(stderr, "%s: Could not encrypt any secret keys.\n",
		    program_name);
		exit(1);
	}
}


/* Initialize the array of public, secret, and encrypted secret keys */
static void
initkeylist(bool_t nomech)
{
	int		mcount;

	if (!nomech) {
		assert(mechs && mechs[0]);
		for (mcount = 0; CURMECH; mcount++)
			;
	} else
		mcount = 1;

	if (!(plist = (char **)malloc(sizeof (char *) * mcount))) {
		fprintf(stderr, "%s: Malloc failure.\n", program_name);
		exit(1);
	}
	if (!(slist = (char **)malloc(sizeof (char *) * mcount))) {
		fprintf(stderr, "%s: Malloc failure.\n", program_name);
		exit(1);
	}
	if (!(clist = (char **)malloc(sizeof (char *) * mcount))) {
		fprintf(stderr, "%s: Malloc failure.\n", program_name);
		exit(1);
	}
}


/* Retrieve public key(s) */
static void
getpublics()
{
	int		mcount;
	int		pcount = 0;

	if (mechs) {
		for (mcount = 0; CURMECH; mcount++) {
			char		*public;
			size_t		hexkeylen;

			hexkeylen = ((CURMECH->keylen / 8) * 2) + 1;
			if (!(public = (char *)malloc(hexkeylen))) {
				fprintf(stderr, "%s: Malloc failure.\n",
				    program_name);
				exit(1);
			}
			if (!getpublickey_g(netname, CURMECH->keylen,
			    CURMECH->algtype, public,
			    hexkeylen)) {
				/* Could not get public key */
				fprintf(stderr,
				    "Could not get %s public key.\n",
				    VALID_ALIAS(CURMECH->alias) ?
				    CURMECH->alias : "");
				free(public);
				public = NULL;
			} else
				pcount++;

			plist[mcount] = public;
		}
	} else {
		char		*public;

		if (!(public = (char *)malloc(HEXKEYBYTES + 1))) {
			fprintf(stderr, "%s: Malloc failure.\n", program_name);
			exit(1);
		}
		if (!getpublickey(netname, public)) {
			free(public);
			public = NULL;
		} else
			pcount++;

		plist[0] = public;
	}

	if (!pcount) {
		fprintf(stderr, "%s: cannot get any public keys for %s.\n",
		    program_name, pw->pw_name);
		error_msg();
		fprintf(stderr,
	"Make sure that the public keys are stored in the domain %s.\n",
		    local_domain);
		exit(1);
	}
}


/* Generate a new set of public/secret key pair(s) */
static void
makenewkeys()
{
	int		mcount;

	if (mechs) {
		for (mcount = 0; CURMECH; mcount++) {
			char		*public, *secret;
			size_t		hexkeylen;

			if (slist[mcount])
				free(slist[mcount]);

			hexkeylen = ((CURMECH->keylen / 8) * 2) + 1;

			if (!(public = malloc(hexkeylen))) {
				fprintf(stderr, "%s: Malloc failure.\n",
				    program_name);
				exit(1);
			}
			if (!(secret = malloc(hexkeylen))) {
				fprintf(stderr, "%s: Malloc failure.\n",
				    program_name);
				exit(1);
			}

			if (!(__gen_dhkeys_g(public, secret, CURMECH->keylen,
			    CURMECH->algtype, short_login_pw))) {
				/* Could not generate key pair */
				fprintf(stderr,
				"WARNING  Could not generate key pair %s\n",
				    VALID_ALIAS(CURMECH->alias) ?
				    CURMECH->alias : "");
				free(public);
				free(secret);
				public = NULL;
				secret = NULL;
			}

			plist[mcount] = public;
			slist[mcount] = secret;
		}
	} else {
		char		*public, *secret;
		if (slist[0])
			free(slist[0]);

		if (!(public = malloc(HEXKEYBYTES + 1))) {
			fprintf(stderr, "%s: Malloc failure.\n", program_name);
			exit(1);
		}
		if (!(secret = malloc(HEXKEYBYTES + 1))) {
			fprintf(stderr, "%s: Malloc failure.\n", program_name);
			exit(1);
		}

		__gen_dhkeys(public, secret, short_login_pw);

		plist[0] = public;
		slist[0] = secret;
	}
}


/*
 * Make sure that the entered Secure-RPC password(s) match the login
 * password
 */
static void
cmp_passwd()
{
	char	baseprompt[] = "Please enter the login password for";
	char	prompt[BUFSIZ];
	char	*en_login_pw = spw->sp_pwdp;
	char	short_en_login_pw[DESCREDPASSLEN + 1];
	char	*try_en_login_pw;
	bool_t	pwmatch = FALSE;
	int	done = 0, tries = 0, pcount;

	snprintf(prompt, BUFSIZ, "%s %s:", baseprompt, pw->pw_name);

	(void) strlcpy(short_en_login_pw, en_login_pw,
	    sizeof (short_en_login_pw));

	if (en_login_pw && (strlen(en_login_pw) != 0)) {
		for (pcount = 0; pcount < rpc_pw_count; pcount++) {
			char	*try_en_rpc_pw;

		try_en_rpc_pw = crypt(rpc_pws[pcount], short_en_login_pw);
			if (strcmp(try_en_rpc_pw, short_en_login_pw) == 0) {
				login_pw = rpc_pws[pcount];
				(void) strlcpy(short_login_pw, login_pw,
				    sizeof (short_login_pw));
				pwmatch = TRUE;
				break;
			}
		}
		if (!pwmatch) {
			/* pw don't match */
			while (!done) {
				/* ask for the pw */
				login_pw = getpassphrase(prompt);
				(void) strlcpy(short_login_pw, login_pw,
				    sizeof (short_login_pw));
				if (login_pw && strlen(login_pw)) {
					/* pw was not empty */
					try_en_login_pw = crypt(login_pw,
					    en_login_pw);
					/* compare the pw's */
					if (!(strcmp(try_en_login_pw,
					    en_login_pw))) {
						/* pw was correct */
						return;
					} else {
						/* pw was wrong */
						if (tries++) {
							/* Sorry */
							fprintf(stderr,
							    "Sorry.\n");
							exit(1);
						} else {
							/* Try again */
							snprintf(prompt,
							    BUFSIZ,
							"Try again. %s %s:",
							    baseprompt,
							    pw->pw_name);
						}
					}
				} else {
					/* pw was empty */
					if (tries++) {
						/* Unchanged */
						fprintf(stderr,
					"%s: key-pair(s) unchanged for %s.\n",
						    program_name,
						    pw->pw_name);
						exit(1);
					} else {
						/* Need a password */
						snprintf(prompt, BUFSIZ,
						"Need a password. %s %s:",
						    baseprompt,
						    pw->pw_name);
					}
				}
			}
		}
		/* pw match */
		return;
	} else {
		/* no pw found */
		fprintf(stderr,
		"%s: no passwd found for %s in the shadow passwd entry.\n",
		    program_name, pw->pw_name);
		exit(1);
	}
}


/* Prompt the user for a Secure-RPC password and store it in a cache. */
static void
getrpcpws(char *flavor)
{
	char		*cur_pw = NULL;
	char		prompt[BUFSIZ + 1];

	if (flavor)
		snprintf(prompt, BUFSIZ,
		    "Please enter the %s Secure-RPC password for %s:",
		    flavor, pw->pw_name);
	else
		snprintf(prompt, BUFSIZ,
		    "Please enter the Secure-RPC password for %s:",
		    pw->pw_name);

	cur_pw = getpass(prompt);
	if (!cur_pw) {
		/* No changes */
		fprintf(stderr, "%s: key-pair(s) unchanged for %s.\n",
		    program_name, pw->pw_name);
		exit(1);
	}

	rpc_pw_count++;
	if (!(rpc_pws =
	    (char **)realloc(rpc_pws, sizeof (char *) * rpc_pw_count))) {
		fprintf(stderr, "%s: Realloc failure.\n", program_name);
		exit(1);
	}
rpc_pws[rpc_pw_count - 1] = cur_pw;
}


/* Retrieve the secret key(s) for the user and attempt to decrypt them */
static void
getsecrets()
{
	int		mcount, scount = 0;
	int		tries = 0;

	getrpcpws(NULL);

	if (mechs) {
		for (mcount = 0; CURMECH; mcount++) {
			char		*secret;
			int		pcount;
			size_t		hexkeylen;

			hexkeylen = ((CURMECH->keylen / 8) * 2) + 1;
			if (!(secret = (char *)calloc(hexkeylen,
			    sizeof (char)))) {
				fprintf(stderr, "%s: Malloc failure.\n",
				    program_name);
				exit(1);
			}

			for (pcount = 0; pcount < rpc_pw_count; pcount++) {
				if (!getsecretkey_g(netname, CURMECH->keylen,
				    CURMECH->algtype, secret,
				    hexkeylen,
				    rpc_pws[pcount]))
					continue;

				if (secret[0] == 0)
					continue;
				else
					break;
			}

			tries = 0;
		getsecrets_tryagain_g:
			if (secret[0] == 0) {
				if (!tries) {
					/*
					 * No existing pw can decrypt
					 * secret key
					 */
					getrpcpws(CURMECH->alias);
					if (!getsecretkey_g(netname,
					    CURMECH->keylen,
					    CURMECH->algtype,
					    secret,
					    hexkeylen,
					    rpc_pws[pcount])) {
						/*
						 * Could not retreive
						 * secret key, abort
						 */
						free(secret);
						secret = NULL;
						goto getsecrets_abort;
					}

					if (secret[0] == 0) {
						/* Still no go, ask again */
						free(rpc_pws[pcount]);
						rpc_pw_count--;
						tries++;
						printf("Try again. ");
						fflush(stdout);
						goto getsecrets_tryagain_g;
					} else
						scount++;
				} else {
					fprintf(stderr,
					"%s: key-pair unchanged for %s.\n",
					    program_name, pw->pw_name);
					exit(1);
				}
			} else
				scount++;

		getsecrets_abort:
			slist[mcount] = secret;
		}
	} else {
		char		*secret = NULL;

		if (!(secret = (char *)malloc(HEXKEYBYTES + 1))) {
			fprintf(stderr, "%s: Malloc failure.\n", program_name);
			exit(1);
		}
	getsecrets_tryagain:
		if (!getsecretkey(netname, secret, rpc_pws[0])) {
			fprintf(stderr,
			    "%s: could not get secret key for '%s'\n",
			    program_name, netname);
			exit(1);
		}

		if (secret[0] == 0) {
			if (!tries) {
				free(rpc_pws[0]);
				rpc_pw_count = 0;
				tries++;
				printf("Try again. ");
				fflush(stdout);
				getrpcpws(NULL);
				goto getsecrets_tryagain;
			} else {
				fprintf(stderr,
				    "%s: key-pair unchanged for %s.\n",
				    program_name, pw->pw_name);
				exit(1);
			}
		}

		slist[0] = secret;
		return;
	}

	if (!scount) {
		(void) fprintf(stderr,
		"%s: could not get nor decrypt any secret keys for '%s'\n",
		    program_name, netname);
		error_msg();
		exit(1);
	}
}


/* Register AUTH_DES secret key with keyserv */
static void
keylogin_des()
{
	char			*secret = slist[0];
	struct key_netstarg	netst;

	/*
	 * try to revoke the existing key/credentials, assuming
	 * one exists.  this will effectively mark "stale" any
	 * cached credientials...
	 */
	if (key_setsecret(secret) < 0) {
		return;
	}

#ifdef NFS_AUTH
	/*
	 * it looks like a credential already existed, so try and
	 * revoke any lingering Secure-NFS privledges.
	 */

	nra.authtype = AUTH_DES;
	nra.uid = getuid();

	if (_nfssys(NFS_REVAUTH, &nra) < 0)
		perror("Warning: NFS credentials not destroyed");
#endif /* NFS_AUTH */

	(void) memcpy(netst.st_priv_key, secret, HEXKEYBYTES);

	netst.st_pub_key[0] = '\0';
	netst.st_netname = strdup(netname);

	/* do actual key login */
	if (key_setnet(&netst) < 0) {
		fprintf(stderr, "Could not set %s's secret key\n", netname);
		fprintf(stderr, "May be the keyserv is down?\n");
	}
}


/* Register a secret key with the keyserv */
static void
keylogin(keylen_t keylen, algtype_t algtype)
{
	int	mcount;

	if (mechs) {
		for (mcount = 0; CURMECH; mcount++) {
			if (keylen == CURMECH->keylen &&
			    algtype == CURMECH->algtype) {
				if (key_setnet_g(netname, slist[mcount],
				    CURMECH->keylen,
				    NULL, 0,
				    CURMECH->algtype)
				    < 0)
					fprintf(stderr,
					"Could not set %s's %s secret key\n",
					    netname,
					    VALID_ALIAS(CURMECH->alias) ?
					    CURMECH->alias : "");
			}
		}
	} else {
		if (keylen == 192 && algtype == 0)
			keylogin_des();
	}
}


/*
 * fgets is "broken" in that if it reads a NUL character it will
 * always return EOF for all reads, even when there is data left in
 * the file.  This replacement can deal with NUL's in a calm, rational
 * manner.
 */
static char *
fgets_ignorenul(char *s, int n, FILE *stream)
{
	int fildes = fileno(stream);
	int i = 0;
	int rs = 0;
	char c;

	if (fildes < 0)
		return (NULL);

	while (i < n - 1) {
		rs = read(fildes, &c, 1);
		switch (rs) {
		case 1:
			break;
		case 0:
			/* EOF */
			if (i > 0)
				s[i] = '\0';
			return (NULL);
			break;
		default:
			return (NULL);
		}
		switch (c) {
		case '\0':
			break;
		case '\n':
			s[i] = c;
			s[++i] = '\0';
			return (s);
		default:
		if (c != '\0')
			s[i++] = c;
		}
	}
	s[i] = '\0';
	return (s);
}


/* Write unencrypted secret key into root key file */
static void
write_rootkey(char *secret, char *flavor, keylen_t keylen, algtype_t algtype)
{
	char		line[MAXROOTKEY_LINE_LEN];
	char		keyent[MAXROOTKEY_LEN];
	algtype_t	atent;
	int		rootfd, bakfd, hexkeybytes;
	bool_t		lineone = TRUE;
	bool_t		gotit = FALSE;
	FILE		*rootfile, *bakfile;

	unlink(ROOTKEY_FILE_BACKUP);
	if ((rename(ROOTKEY_FILE, ROOTKEY_FILE_BACKUP)) < 0) {
		if ((bakfd = creat(ROOTKEY_FILE_BACKUP, 0600)) < 0) {
			perror("Could not create /etc/.rootkey.bak");
			goto rootkey_err;
		}
		close(bakfd);
	}

	if ((rootfd = open(ROOTKEY_FILE, O_WRONLY+O_CREAT, 0600)) < 0) {
		perror("Could not open /etc/.rootkey for writing");
		fprintf(stderr,
		    "Attempting to restore original /etc/.rootkey\n");
		rename(ROOTKEY_FILE_BACKUP, ROOTKEY_FILE);
		goto rootkey_err;
	}
	if (!(rootfile = fdopen(rootfd, "w"))) {
		perror("Could not open /etc/.rootkey for writing");
		fprintf(stderr,
		    "Attempting to restore original /etc/.rootkey\n");
		close(rootfd);
		unlink(ROOTKEY_FILE);
		rename(ROOTKEY_FILE_BACKUP, ROOTKEY_FILE);
		goto rootkey_err;
	}
	if (!(bakfile = fopen(ROOTKEY_FILE_BACKUP, "r"))) {
		perror("Could not open /etc/.rootkey.bak for reading");
		fprintf(stderr,
		    "Attempting to restore original /etc/.rootkey\n");
		fclose(rootfile);
		unlink(ROOTKEY_FILE);
		rename(ROOTKEY_FILE_BACKUP, ROOTKEY_FILE);
		goto rootkey_err;
	}

	hexkeybytes = ((keylen + 7) / 8) * 2;

	while (fgets_ignorenul(line, MAXROOTKEY_LINE_LEN, bakfile)) {
		if (sscanf(line, "%s %d", keyent, &atent) < 2) {
			/*
			 * No encryption algorithm found in the file
			 * (atent) so default to DES.
			 */
			atent = AUTH_DES_ALGTYPE;
		}
		/*
		 * 192-bit keys always go on the first line
		 */
		if (lineone) {
			lineone = FALSE;
			if (keylen == 192) {
				gotit = TRUE;
				fprintf(rootfile, "%s\n", secret);
			} else
				fprintf(rootfile, "%s", line);
			fflush(rootfile);
		} else {
			if ((strlen(keyent) == hexkeybytes) &&
			    (atent == algtype)) {
				/*
				 * Silently remove lines with the same
				 * keylen/algtype
				 */
				if (gotit)
					continue;
				else
					gotit = TRUE;

				fprintf(rootfile, "%s %d\n", secret, algtype);
			} else
				fprintf(rootfile, "%s", line);
			fflush(rootfile);
		}
	}

	/* Append key to rootkey file */
	if (!gotit) {
		if (keylen == 192)
			fprintf(rootfile, "%s\n", secret);
		else {
			if (lineone)
				fprintf(rootfile, "\n");
			fprintf(rootfile, "%s %d\n", secret, algtype);
		}
	}
	fflush(rootfile);
	fclose(rootfile);
	fclose(bakfile);
	unlink(ROOTKEY_FILE_BACKUP);
	return;

rootkey_err:
	fprintf(stderr, "WARNING: Could not write %s key to /etc/.rootkey\n",
	    flavor);
}

/* Store new key information in the specified name service */
static void
storekeys()
{
	int		mcount, ucount = 0;
	char		*ypmaster, *ypdomain = NULL, pkent[MAXPKENTLEN];
	nis_name	nis_princ;


	/* Setup */
	switch (dest_service) {
	case PK_LDAP:
		break;
	case PK_YP:
		yp_get_default_domain(&ypdomain);
		if (yp_master(ypdomain, PKMAP, &ypmaster) != 0) {
			fprintf(stderr,
			"%s: cannot find master of NIS publickey database\n",
			    program_name);
			exit(1);
		}
		fprintf(stdout,
		    "Sending key change request to %s ...\n", ypmaster);
		break;
	case PK_FILES:
		if (geteuid() != 0) {
			fprintf(stderr,
		"%s: non-root users cannot change their key-pair in %s\n",
			    program_name, PKFILE);
			exit(1);
		}
		break;
	default:
		fprintf(stderr,
		    "could not update; database %d unknown\n",
		    dest_service);
		exit(1);
	}

	if (mechs) {
		for (mcount = 0; CURMECH; mcount++) {
			char		authtype[MECH_MAXATNAME];

			if (!plist[mcount] && !clist[mcount])
				continue;

			__nis_mechalias2authtype(CURMECH->alias, authtype,
			    MECH_MAXATNAME);
			if (!authtype) {
				fprintf(stderr,
				"Could not generate auth_type for %s.\n",
				    CURMECH->alias);
				continue;
			}

			snprintf(pkent, MAXPKENTLEN, "%s:%s:%d",
			    plist[mcount], clist[mcount],
			    CURMECH->algtype);

			switch (dest_service) {
			case PK_LDAP:
				if (ldap_update(CURMECH->alias, netname,
				    plist[mcount], clist[mcount],
				    login_pw))
					fprintf(stderr,
			"%s: unable to update %s key in LDAP database\n",
					    program_name, authtype);
				else
					ucount++;
				break;

			case PK_YP:
				/* Should never get here. */
				break;

			case PK_FILES:
				/* Should never get here. */
				break;
			}
		}
	} else {
		int	status = 0;

		assert(plist[0] && clist[0]);
		snprintf(pkent, MAXPKENTLEN, "%s:%s", plist[0], clist[0]);

		switch (dest_service) {
		case PK_LDAP:
			if (ldap_update("dh192-0", netname,
			    plist[0], clist[0],
			    login_pw)) {
				fprintf(stderr,
			"%s: unable to update %s key in LDAP database\n",
				    program_name);
				exit(1);
			}
			break;

		case PK_YP:
			if (status = yp_update(ypdomain, PKMAP,
			    YPOP_STORE, netname,
			    strlen(netname), pkent,
			    strlen(pkent))) {
				fprintf(stderr,
				"%s: unable to update NIS database (%u): %s\n",
				    program_name, status,
				    yperr_string(status));
				exit(1);
			}
			break;

		case PK_FILES:
			if (localupdate(netname, PKFILE, YPOP_STORE, pkent)) {
				fprintf(stderr,
			"%s: hence, unable to update publickey database\n",
				    program_name);
				exit(1);
			}
			break;

		default:
			/* Should never get here */
			assert(0);
		}
		return;
	}
	if (!ucount) {
		fprintf(stderr, "%s: unable to update any key-pairs for %s.\n",
		    program_name, pw->pw_name);
		exit(1);
	}
}

void
addmechtolist(char *mechtype)
{
	mechanism_t	**realmechlist;
	int		i;

	if (realmechlist = __nis_get_mechanisms(FALSE)) {
		/* Match requested mech with list */
		for (i = 0; realmechlist[i]; i++) {
			if (realmechlist[i]->alias)
				if (strcmp(realmechlist[i]->alias, mechtype)
				    == 0) {
					/*
					 * Match, add it to the mechs.
					 * Don't worry about qop or
					 * secserv since they are not
					 * used by chkey.
					 */
					numspecmech++;
					if ((mechs =
					    (mechanism_t **)realloc(mechs,
					    sizeof (mechanism_t *) *
					    (numspecmech + 1))) == NULL) {
						perror("Can not change keys");
						exit(1);
					}

					if ((mechs[numspecmech - 1] =
					    (mechanism_t *)malloc(
					    sizeof (mechanism_t))) == NULL) {
						perror("Can not change keys");
						exit(1);
					}
					if (realmechlist[i]->mechname)
					mechs[numspecmech - 1]->mechname =
					    strdup(realmechlist[i]->mechname);
					if (realmechlist[i]->alias)
					mechs[numspecmech - 1]->alias =
					    strdup(realmechlist[i]->alias);
					mechs[numspecmech - 1]->keylen =
					    realmechlist[i]->keylen;
					mechs[numspecmech - 1]->algtype =
					    realmechlist[i]->algtype;
					mechs[numspecmech] = NULL;
					__nis_release_mechanisms(realmechlist);
					return;
				}
		}

		fprintf(stderr,
		"WARNING: Mechanism '%s' not configured, skipping...\n",
		    mechtype);
		__nis_release_mechanisms(realmechlist);
		return;
	}
	fprintf(stderr,
	"WARNING: Mechanism '%s' not configured, skipping...\n",
	    mechtype);
}


int
main(int argc, char **argv)
{
	int		c, mcount;
	uid_t		uid;
	uid_t		orig_euid;
	char		*service = NULL;
	program_name = argv[0];

	mechs = __nis_get_mechanisms(FALSE);

	while ((c = getopt(argc, argv, "fps:m:")) != -1) {
		switch (c) {
		case 'f':
			/*
			 * Not documented as of on1093.
			 * Temporarily supported
			 */
			force++;
			break;
		case 'p':
			makenew = FALSE;
			break;
		case 's':
			if (!service)
				service = strdup(optarg);
			else
				usage();
			break;
		case 'm':
			if (mechs && specmech == FALSE) {
				__nis_release_mechanisms(mechs);
				mechs = NULL;
			}
			specmech = TRUE;
			addmechtolist(optarg);
			break;
		default:
			usage();
		}
	}

	if (optind < argc)
		usage();

	dest_service = get_pk_source(service);

	if (!(netname = malloc(MAXNETNAMELEN + 1))) {
		fprintf(stderr, "%s: Malloc failure.\n", program_name);
		exit(1);
	}
	if (!__getnetnamebyuid(netname, uid = getuid())) {
		fprintf(stderr, "%s: cannot generate netname for uid %d\n",
		    program_name, uid);
		exit(1);
	}
	sec_domain = strdup(strchr(netname, '@') + 1);
	getdomainname(local_domain, MAXNETNAMELEN);

	if (makenew)
		fprintf(stdout, "Generating new key for '%s'.\n", netname);
	else
		fprintf(stdout, "Reencrypting key for '%s'.\n", netname);

	if (mechs) {
		if (dest_service == PK_YP || dest_service == PK_FILES) {
			fprintf(stderr,
		"%s: can not add non-DES public keys to %s, skipping.\n",
			    program_name, service);
			__nis_release_mechanisms(mechs);
			mechs = NULL;
			initkeylist(TRUE);
		} else
			initkeylist(FALSE);
	} else
		initkeylist(TRUE);

	uid = getuid();
	orig_euid = geteuid();

	/* Get password information */
	if ((pw = getpwuid(uid)) == NULL) {
		fprintf(stderr,
		"%s: Can not find passwd information for %d.\n",
		    program_name, uid);
		exit(1);
	}

	/* Set eUID to user */
	seteuid(uid);

	/* Obtain a list of decrypted secret keys */
	getsecrets();

	/* Keylogin user if not already done */
	if (mechs) {
		int mcount;

		for (mcount = 0; CURMECH; mcount++) {
			keylen_t	keylen = CURMECH->keylen;
			algtype_t	algtype = CURMECH->algtype;

			if (!key_secretkey_is_set_g(keylen, algtype) &&
			    slist[mcount]) {
				keylogin(CURMECH->keylen, CURMECH->algtype);
				if ((uid == 0) && (makenew == FALSE))
					write_rootkey(slist[mcount],
					    VALID_ALIAS(CURMECH->alias) ?
					    CURMECH->alias :
					    "",
					    keylen, algtype);
			}
		}
	} else {
		assert(slist[0]);
		if (!key_secretkey_is_set()) {
			keylogin_des();
			if ((uid == 0) && (makenew == FALSE))
				write_rootkey(slist[0], "des", 192, 0);
		}
	}

	/* Set eUID back to root */
	(void) seteuid(orig_euid);

	/*
	 * Call getspnam() after the keylogin has been done so we have
	 * the best chance of having read access to the encrypted pw.
	 *
	 * The eUID must be 0 for the getspnam() so the name service
	 * switch can handle the following eUID sensitive cases:
	 *
	 *	files/compat:	read /etc/shadow
	 *
	 */
	if ((spw = getspnam(pw->pw_name)) == 0) {

		/* Set eUID back to user */
		(void) seteuid(uid);

		(void) fprintf(stderr,
		"%s: cannot find shadow entry for %s.\n",
		    program_name, pw->pw_name);
		exit(1);
	}

	/* Set eUID back to user */
	(void) seteuid(uid);

	if (strcmp(spw->sp_pwdp, NOPWDRTR) == 0) {
		(void) fprintf(stderr,
		"%s: do not have read access to the passwd field for %s\n",
		    program_name, pw->pw_name);
		exit(1);
	}

	/*
	 * force will be only supported for a while
	 * 	-- it is NOT documented as of s1093
	 */
	if (force) {
		char	*prompt = "Please enter New password:";

		login_pw = getpassphrase(prompt);
		(void) strlcpy(short_login_pw, login_pw,
		    sizeof (short_login_pw));
		if (!login_pw || !(strlen(login_pw))) {
			fprintf(stderr, "%s: key-pair(s) unchanged for %s.\n",
			    program_name, pw->pw_name);
			exit(1);
		}
	} else {
		/*
		 * Reconsile rpc_pws and login_pw.
		 *
		 * This function will either return with login_pw == rpc_pw
		 * (and thus, the new pw to encrypt keys) or it will exit.
		 */
		cmp_passwd();
	}

	if (makenew)
		makenewkeys();
	else
		getpublics();

	encryptkeys();

	storekeys();

	if (makenew) {
		if (uid == 0) {
			if (mechs) {
				for (mcount = 0; CURMECH; mcount++) {
					if (!slist[mcount])
						continue;
					write_rootkey(slist[mcount],
					    CURMECH->alias,
					    CURMECH->keylen,
					    CURMECH->algtype);
				}
			} else {
				assert(slist[0]);
				write_rootkey(slist[0], "des", 192, 0);
			}
		}
		if (mechs) {
			for (mcount = 0; CURMECH; mcount++)
				keylogin(CURMECH->keylen,
				    CURMECH->algtype);
		} else
			keylogin_des();
	}
	return (0);
}