chkey.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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 2004 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.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#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_NISPLUS 3
#define PK_LDAP 4
#define CURMECH mechs[mcount]
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 */
static int add_cred_obj(nis_object *, char *);
static nis_error auth_exists(char *, char *, char *, char *);
static void cmp_passwd();
static nis_error cred_exists(const char *, const char *, const char *);
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 int nisplus_update(nis_name, char *, char *, char *);
static int sanity_checks(char *, char *, char *);
static void storekeys();
static void usage();
static void write_rootkey();
extern char *get_nisplus_principal(char *, uid_t);
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. The Secure RPC domainname is\n\
defined by the directory object stored in the /var/nis/NIS_COLD_START file.\n\
If you need to change this Secure RPC domainname, please use the nisinit(1M)\n\
command with the `-k` option.\n", local_domain, sec_domain);
exit(1);
}
}
static void
usage()
{
fprintf(stderr, "usage: %s [-p] [-s ldap | nisplus | 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, 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);
}
memcpy(crypt, slist[0], HEXKEYBYTES);
memcpy(crypt + HEXKEYBYTES, slist[0], KEYCHECKSUMSIZE);
crypt[HEXKEYBYTES + KEYCHECKSUMSIZE] = 0;
xencrypt(crypt, 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, 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, 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 *try_en_login_pw;
bool_t pwmatch = FALSE;
int done = 0, tries = 0, pcount;
snprintf(prompt, BUFSIZ, "%s %s:", baseprompt, pw->pw_name);
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], en_login_pw);
if (strcmp(try_en_rpc_pw, en_login_pw) == 0) {
login_pw = rpc_pws[pcount];
pwmatch = TRUE;
break;
}
}
if (!pwmatch) {
/* pw don't match */
while (!done) {
/* ask for the pw */
login_pw = getpass(prompt);
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 */
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)) {
sscanf(line, "%s %d", keyent, &atent);
/*
* 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);
}
/* Returns 0 if check fails; 1 if successful. */
static int
sanity_checks(char *nis_princ, char *domain, char *authtype)
{
char netdomainaux[MAXHOSTNAMELEN+1];
char *princdomain, *netdomain;
int len;
/* Sanity check 0. Do we have a nis+ principal name to work with? */
if (nis_princ == NULL) {
(void) fprintf(stderr,
"%s: you must create a \"LOCAL\" credential for '%s' first.\n",
program_name, netname);
(void) fprintf(stderr, "\tSee nisaddcred(1).\n");
return (0);
}
/* Sanity check 0.5. NIS+ principal names must be dotted. */
len = strlen(nis_princ);
if (nis_princ[len-1] != '.') {
(void) fprintf(stderr,
"%s: invalid principal name: '%s' (forgot ending dot?).\n",
program_name, nis_princ);
return (0);
}
/* Sanity check 1. We only deal with one type of netnames. */
if (strncmp(netname, "unix", 4) != 0) {
(void) fprintf(stderr,
"%s: unrecognized netname type: '%s'.\n",
program_name, netname);
return (0);
}
/* Sanity check 2. Should only add DES cred in home domain. */
princdomain = nis_domain_of(nis_princ);
if (strcasecmp(princdomain, domain) != 0) {
(void) fprintf(stderr,
"%s: domain of principal '%s' does not match destination domain '%s'.\n",
program_name, nis_princ, domain);
(void) fprintf(stderr,
"Should only add DES credential of principal in its home domain\n");
return (0);
}
/*
* Sanity check 3: Make sure netname's domain same as principal's
* and don't have extraneous dot at the end.
*/
netdomain = (char *)strchr(netname, '@');
if (! netdomain || netname[strlen(netname)-1] == '.') {
(void) fprintf(stderr, "%s: invalid netname: '%s'. \n",
program_name, netname);
return (0);
}
netdomain++; /* skip '@' */
if (strlcpy(netdomainaux, netdomain, sizeof (netdomainaux)) >=
sizeof (netdomainaux)) {
(void) fprintf(stderr, "%s: net domain name %s is too long\n",
program_name, netdomain);
return (0);
}
if (netdomainaux[strlen(netdomainaux) - 1] != '.') {
if (strlcat(netdomainaux, ".", sizeof (netdomainaux)) >=
sizeof (netdomainaux)) {
(void) fprintf(stderr,
"%s: net domain name %s is too long\n",
program_name, netdomainaux);
return (0);
}
}
if (strcasecmp(princdomain, netdomainaux) != 0) {
(void) fprintf(stderr,
"%s: domain of netname %s should be same as that of principal %s\n",
program_name, netname, nis_princ);
return (0);
}
/* Another principal owns same credentials? (exits if that happens) */
(void) auth_exists(nis_princ, netname, authtype, domain);
return (1); /* all passed */
}
/* 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_NISPLUS:
nis_princ = get_nisplus_principal(nis_local_directory(),
geteuid());
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_NISPLUS:
if (nisplus_update(nis_princ,
authtype,
plist[mcount],
clist[mcount]))
fprintf(stderr,
"%s: unable to update %s key in nisplus 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_NISPLUS:
assert(plist[0] && clist[0]);
if (nisplus_update(nis_princ,
AUTH_DES_AUTH_TYPE,
plist[0],
clist[0])) {
fprintf(stderr,
"%s: unable to update nisplus 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);
}
}
/* Check that someone else don't have the same auth information already */
static
nis_error
auth_exists(char *princname, char *auth_name, char *auth_type, char *domain)
{
char sname[NIS_MAXNAMELEN+1];
nis_result *res;
nis_error status;
char *foundprinc;
(void) sprintf(sname, "[auth_name=%s,auth_type=%s],%s.%s",
auth_name, auth_type, CRED_TABLE, domain);
if (sname[strlen(sname)-1] != '.')
strcat(sname, ".");
/* Don't want FOLLOW_PATH here */
res = nis_list(sname,
MASTER_ONLY+USE_DGRAM+NO_AUTHINFO+FOLLOW_LINKS,
NULL, NULL);
status = res->status;
switch (res->status) {
case NIS_NOTFOUND:
break;
case NIS_TRYAGAIN:
(void) fprintf(stderr,
"%s: NIS+ server busy, try again later.\n",
program_name);
exit(1);
break;
case NIS_PERMISSION:
(void) fprintf(stderr,
"%s: insufficient permission to look up old credentials.\n",
program_name);
exit(1);
break;
case NIS_SUCCESS:
foundprinc = ENTRY_VAL(res->objects.objects_val, 0);
if (nis_dir_cmp(foundprinc, princname) != SAME_NAME) {
(void) fprintf(stderr,
"%s: %s credentials with auth_name '%s' already belong to '%s'.\n",
program_name, auth_type, auth_name, foundprinc);
exit(1);
}
break;
default:
(void) fprintf(stderr,
"%s: error looking at cred table, NIS+ error: %s\n",
program_name, nis_sperrno(res->status));
exit(1);
}
nis_freeresult(res);
return (status);
}
/* Check whether this principal already has this type of credentials */
static nis_error
cred_exists(const char *nisprinc, const char *flavor, const char *domain)
{
char sname[NIS_MAXNAMELEN+1];
nis_result *res;
nis_error status;
snprintf(sname, NIS_MAXNAMELEN,
"[cname=\"%s\",auth_type=%s],%s.%s",
nisprinc, flavor, CRED_TABLE, domain);
if (sname[strlen(sname)-1] != '.')
strcat(sname, ".");
/* Don't want FOLLOW_PATH here */
res = nis_list(sname,
MASTER_ONLY+USE_DGRAM+NO_AUTHINFO+FOLLOW_LINKS,
NULL, NULL);
status = res->status;
switch (status) {
case NIS_NOTFOUND:
break;
case NIS_TRYAGAIN:
fprintf(stderr,
"%s: NIS+ server busy, try again later.\n",
program_name);
exit(1);
break;
case NIS_PERMISSION:
(void) fprintf(stderr,
"%s: insufficient permission to look at credentials table\n",
program_name);
exit(1);
break;
case NIS_SUCCESS:
case NIS_S_SUCCESS:
break;
default:
(void) fprintf(stderr,
"%s: error looking at cred table, NIS+ error: %s\n",
program_name, nis_sperrno(res->status));
exit(1);
}
nis_freeresult(res);
return (status);
}
static int
modify_cred_obj(nis_object *obj, char *domain)
{
int status = 0;
char sname[NIS_MAXNAMELEN+1];
nis_result *res;
(void) sprintf(sname, "%s.%s", CRED_TABLE, domain);
res = nis_modify_entry(sname, obj, 0);
switch (res->status) {
case NIS_TRYAGAIN:
(void) fprintf(stderr,
"%s: NIS+ server busy, try again later.\n",
program_name);
exit(1);
break;
case NIS_PERMISSION:
(void) fprintf(stderr,
"%s: insufficient permission to update credentials.\n",
program_name);
exit(1);
break;
case NIS_SUCCESS:
status = 1;
break;
default:
(void) fprintf(stderr,
"%s: error modifying credential, NIS+ error: %s.\n",
program_name, nis_sperrno(res->status));
exit(1);
}
nis_freeresult(res);
return (status);
}
static int
add_cred_obj(nis_object *obj, char *domain)
{
int status = 0;
char sname[NIS_MAXNAMELEN+1];
nis_result *res;
/* Assume check for cred_exists performed already */
(void) sprintf(sname, "%s.%s", CRED_TABLE, domain);
res = nis_add_entry(sname, obj, 0);
switch (res->status) {
case NIS_TRYAGAIN:
(void) fprintf(stderr,
"%s: NIS+ server busy, try again later.\n",
program_name);
exit(1);
break;
case NIS_PERMISSION:
(void) fprintf(stderr,
"%s: insufficient permission to update credentials.\n",
program_name);
exit(1);
break;
case NIS_SUCCESS:
status = 1;
break;
default:
(void) fprintf(stderr,
"%s: error creating credential, NIS+ error: %s.\n",
program_name, nis_sperrno(res->status));
exit(1);
}
nis_freeresult(res);
return (status);
}
/* Update NIS+ table with new key information */
static int
nisplus_update(nis_name nis_princ, char *authtype, char *public, char *crypt)
{
nis_object *obj = init_entry();
int status;
bool_t addition;
char *userdomain, *cmpdomain, *domain;
if (!(userdomain = strchr(netname, '@'))) {
fprintf(stderr, "%s: invalid netname: '%s'.\n",
program_name, netname);
exit(1);
}
userdomain++;
cmpdomain = strdup(userdomain);
if (cmpdomain[strlen(cmpdomain) - 1] != '.')
strcat(cmpdomain, ".");
domain = nis_domain_of(nis_princ);
if (strcasecmp(domain, cmpdomain) != 0)
domain = nis_local_directory();
if (!sanity_checks(nis_princ, domain, authtype))
exit(1);
addition = (cred_exists(nis_princ, authtype, domain) == NIS_NOTFOUND);
ENTRY_VAL(obj, 0) = nis_princ;
ENTRY_LEN(obj, 0) = strlen(nis_princ) + 1;
ENTRY_VAL(obj, 1) = authtype;
ENTRY_LEN(obj, 1) = strlen(authtype) + 1;
ENTRY_VAL(obj, 2) = netname;
ENTRY_LEN(obj, 2) = strlen(netname) + 1;
ENTRY_VAL(obj, 3) = public;
ENTRY_LEN(obj, 3) = strlen(public) + 1;
ENTRY_VAL(obj, 4) = crypt;
ENTRY_LEN(obj, 4) = strlen(crypt) + 1;
if (addition) {
obj->zo_owner = nis_princ;
obj->zo_group = nis_local_group();
obj->zo_domain = domain;
/* owner: r, group: rmcd */
obj->zo_access = ((NIS_READ_ACC<<16)|
(NIS_READ_ACC|NIS_MODIFY_ACC|NIS_CREATE_ACC|
NIS_DESTROY_ACC)<<8);
status = add_cred_obj(obj, domain);
} else {
obj->EN_data.en_cols.en_cols_val[3].ec_flags |= EN_MODIFIED;
obj->EN_data.en_cols.en_cols_val[4].ec_flags |= EN_MODIFIED;
status = modify_cred_obj(obj, domain);
}
return (status == 1 ? 0 : 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);
}
void
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
*
* nisplus: try to read the encrypted pw as the root
* principal and if that fails, and if the
* user's secret key is set, seteuid(user)
* and retry the read.
*/
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, "*NP*") == 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 = getpass(prompt);
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();
}
exit(0);
}