chkey.c revision 36e852a172cba914383d7341c988128b2c667fbd
/*
* 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 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 <unistd.h>
#include <rpc/key_prot.h>
#include <rpcsvc/nis_dhext.h>
#include <nsswitch.h>
#define PK_FILES 1
#define PK_YP 2
#define PK_LDAP 4
#define DESCREDPASSLEN sizeof (des_block)
static char CRED_TABLE[] = "cred.org_dir";
static char PKMAP[] = "publickey.byname";
#define MAXHOSTNAMELEN 256
#define ROOTKEY_FILE "/etc/.rootkey"
#define ROOTKEY_FILE_BACKUP "/etc/.rootkey.bak"
#define MAXROOTKEY_LEN 4096
/* Should last up to 16384-bit keys */
#define MAXPKENTLEN 8500
int dest_service = 0; /* To which nameservice do we store key(s) */
char *program_name;
int numspecmech = 0; /* Number of mechanisms specified */
char *sec_domain = NULL;
int rpc_pw_count = 0; /* Number of passwords entered by user */
/* 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_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
{
if (sec_domain && *sec_domain &&
"The system default domain '%s' is different from the Secure RPC\n\
exit(1);
}
}
static void
usage()
{
exit(1);
}
/* Encrypt secret key(s) with login_pw */
static void
{
if (mechs) {
/* Could not crypt key */
} else
ccount++;
}
} else {
if (!(crypt =
exit(1);
}
ccount++;
}
if (!ccount) {
exit(1);
}
}
/* Initialize the array of public, secret, and encrypted secret keys */
static void
{
int mcount;
if (!nomech) {
;
} else
mcount = 1;
exit(1);
}
exit(1);
}
exit(1);
}
}
/* Retrieve public key(s) */
static void
{
int mcount;
int pcount = 0;
if (mechs) {
char *public;
exit(1);
}
hexkeylen)) {
/* Could not get public key */
"Could not get %s public key.\n",
} else
pcount++;
}
} else {
char *public;
exit(1);
}
} else
pcount++;
}
if (!pcount) {
error_msg();
"Make sure that the public keys are stored in the domain %s.\n",
exit(1);
}
}
static void
{
int mcount;
if (mechs) {
exit(1);
}
exit(1);
}
/* Could not generate key pair */
"WARNING Could not generate key pair %s\n",
}
}
} else {
if (slist[0])
exit(1);
}
exit(1);
}
}
}
/*
* Make sure that the entered Secure-RPC password(s) match the login
* password
*/
static void
{
char baseprompt[] = "Please enter the login password for";
char *try_en_login_pw;
sizeof (short_en_login_pw));
char *try_en_rpc_pw;
sizeof (short_login_pw));
break;
}
}
if (!pwmatch) {
/* pw don't match */
while (!done) {
/* ask for the pw */
sizeof (short_login_pw));
/* pw was not empty */
/* compare the pw's */
if (!(strcmp(try_en_login_pw,
en_login_pw))) {
/* pw was correct */
return;
} else {
/* pw was wrong */
if (tries++) {
/* Sorry */
"Sorry.\n");
exit(1);
} else {
/* Try again */
"Try again. %s %s:",
}
}
} else {
/* pw was empty */
if (tries++) {
/* Unchanged */
"%s: key-pair(s) unchanged for %s.\n",
exit(1);
} else {
/* Need a password */
"Need a password. %s %s:",
}
}
}
}
/* pw match */
return;
} else {
/* no pw found */
"%s: no passwd found for %s in the shadow passwd entry.\n",
exit(1);
}
}
/* Prompt the user for a Secure-RPC password and store it in a cache. */
static void
{
if (flavor)
"Please enter the %s Secure-RPC password for %s:",
else
"Please enter the Secure-RPC password for %s:",
if (!cur_pw) {
/* No changes */
exit(1);
}
rpc_pw_count++;
if (!(rpc_pws =
exit(1);
}
}
/* Retrieve the secret key(s) for the user and attempt to decrypt them */
static void
{
int tries = 0;
if (mechs) {
char *secret;
int pcount;
sizeof (char)))) {
exit(1);
}
continue;
if (secret[0] == 0)
continue;
else
break;
}
tries = 0;
if (secret[0] == 0) {
if (!tries) {
/*
* No existing pw can decrypt
* secret key
*/
if (!getsecretkey_g(netname,
/*
* Could not retreive
* secret key, abort
*/
goto getsecrets_abort;
}
if (secret[0] == 0) {
/* Still no go, ask again */
rpc_pw_count--;
tries++;
printf("Try again. ");
goto getsecrets_tryagain_g;
} else
scount++;
} else {
"%s: key-pair unchanged for %s.\n",
exit(1);
}
} else
scount++;
}
} else {
exit(1);
}
"%s: could not get secret key for '%s'\n",
exit(1);
}
if (secret[0] == 0) {
if (!tries) {
rpc_pw_count = 0;
tries++;
printf("Try again. ");
goto getsecrets_tryagain;
} else {
"%s: key-pair unchanged for %s.\n",
exit(1);
}
}
return;
}
if (!scount) {
"%s: could not get nor decrypt any secret keys for '%s'\n",
error_msg();
exit(1);
}
}
/* Register AUTH_DES secret key with keyserv */
static void
{
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.
*/
perror("Warning: NFS credentials not destroyed");
#endif /* NFS_AUTH */
/* do actual key login */
if (key_setnet(&netst) < 0) {
}
}
/* Register a secret key with the keyserv */
static void
{
int mcount;
if (mechs) {
NULL, 0,
< 0)
"Could not set %s's %s secret key\n",
}
}
} else {
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 *
{
int i = 0;
int rs = 0;
char c;
if (fildes < 0)
return (NULL);
while (i < n - 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
{
char line[MAXROOTKEY_LINE_LEN];
char keyent[MAXROOTKEY_LEN];
perror("Could not create /etc/.rootkey.bak");
goto rootkey_err;
}
}
perror("Could not open /etc/.rootkey for writing");
"Attempting to restore original /etc/.rootkey\n");
goto rootkey_err;
}
perror("Could not open /etc/.rootkey for writing");
"Attempting to restore original /etc/.rootkey\n");
goto rootkey_err;
}
perror("Could not open /etc/.rootkey.bak for reading");
"Attempting to restore original /etc/.rootkey\n");
goto rootkey_err;
}
/*
* No encryption algorithm found in the file
* (atent) so default to DES.
*/
}
/*
* 192-bit keys always go on the first line
*/
if (lineone) {
if (keylen == 192) {
} else
} else {
/*
* Silently remove lines with the same
*/
if (gotit)
continue;
else
} else
}
}
/* Append key to rootkey file */
if (!gotit) {
if (keylen == 192)
else {
if (lineone)
}
}
return;
flavor);
}
/* Store new key information in the specified name service */
static void
{
/* Setup */
switch (dest_service) {
case PK_LDAP:
break;
case PK_YP:
"%s: cannot find master of NIS publickey database\n",
exit(1);
}
"Sending key change request to %s ...\n", ypmaster);
break;
case PK_FILES:
if (geteuid() != 0) {
"%s: non-root users cannot change their key-pair in %s\n",
exit(1);
}
break;
default:
"could not update; database %d unknown\n",
exit(1);
}
if (mechs) {
char authtype[MECH_MAXATNAME];
continue;
if (!authtype) {
"Could not generate auth_type for %s.\n",
continue;
}
switch (dest_service) {
case PK_LDAP:
login_pw))
"%s: unable to update %s key in LDAP database\n",
else
ucount++;
break;
case PK_YP:
/* Should never get here. */
break;
case PK_FILES:
/* Should never get here. */
break;
}
}
} else {
int status = 0;
switch (dest_service) {
case PK_LDAP:
login_pw)) {
"%s: unable to update %s key in LDAP database\n",
exit(1);
}
break;
case PK_YP:
"%s: unable to update NIS database (%u): %s\n",
exit(1);
}
break;
case PK_FILES:
"%s: hence, unable to update publickey database\n",
exit(1);
}
break;
default:
/* Should never get here */
assert(0);
}
return;
}
if (!ucount) {
exit(1);
}
}
void
addmechtolist(char *mechtype)
{
int i;
/* Match requested mech with list */
for (i = 0; realmechlist[i]; i++) {
if (realmechlist[i]->alias)
== 0) {
/*
* Match, add it to the mechs.
* Don't worry about qop or
* secserv since they are not
* used by chkey.
*/
numspecmech++;
if ((mechs =
sizeof (mechanism_t *) *
perror("Can not change keys");
exit(1);
}
(mechanism_t *)malloc(
sizeof (mechanism_t))) == NULL) {
perror("Can not change keys");
exit(1);
}
if (realmechlist[i]->mechname)
if (realmechlist[i]->alias)
realmechlist[i]->keylen;
realmechlist[i]->algtype;
return;
}
}
"WARNING: Mechanism '%s' not configured, skipping...\n",
mechtype);
return;
}
"WARNING: Mechanism '%s' not configured, skipping...\n",
mechtype);
}
int
{
int c, mcount;
program_name = argv[0];
switch (c) {
case 'f':
/*
* Not documented as of on1093.
* Temporarily supported
*/
force++;
break;
case 'p':
break;
case 's':
if (!service)
else
usage();
break;
case 'm':
}
break;
default:
usage();
}
}
usage();
exit(1);
}
program_name, uid);
exit(1);
}
if (makenew)
else
if (mechs) {
"%s: can not add non-DES public keys to %s, skipping.\n",
} else
} else
/* Get password information */
"%s: Can not find passwd information for %d.\n",
program_name, uid);
exit(1);
}
/* Set eUID to user */
/* Obtain a list of decrypted secret keys */
getsecrets();
/* Keylogin user if not already done */
if (mechs) {
int mcount;
"",
}
}
} else {
if (!key_secretkey_is_set()) {
keylogin_des();
}
}
/* Set eUID back to root */
/*
* 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:
*
*
*/
/* Set eUID back to user */
"%s: cannot find shadow entry for %s.\n",
exit(1);
}
/* Set eUID back to user */
"%s: do not have read access to the passwd field for %s\n",
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:";
sizeof (short_login_pw));
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) {
continue;
}
} else {
}
}
if (mechs) {
} else
keylogin_des();
}
return (0);
}