root/usr/src/cmd/keyserv/chkey.c
/*
 * 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);
}