Home | History | Annotate | Download | only in keyserv
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
     27 /*	  All Rights Reserved  	*/
     28 
     29 /*
     30  * University Copyright- Copyright (c) 1982, 1986, 1988
     31  * The Regents of the University of California
     32  * All Rights Reserved
     33  *
     34  * University Acknowledgment- Portions of this document are derived from
     35  * software developed by the University of California, Berkeley, and its
     36  * contributors.
     37  */
     38 
     39 
     40 #include <assert.h>
     41 #include <stdio.h>
     42 #include <stdlib.h>
     43 #include <string.h>
     44 #include <pwd.h>
     45 #include <shadow.h>
     46 #include <crypt.h>
     47 #include <sys/types.h>
     48 #include <unistd.h>
     49 #include <rpc/rpc.h>
     50 #include <rpc/key_prot.h>
     51 #include <rpcsvc/nis.h>
     52 #include <rpcsvc/nis_dhext.h>
     53 #include <rpcsvc/ypclnt.h>
     54 #include <nsswitch.h>
     55 
     56 #define	PK_FILES	1
     57 #define	PK_YP		2
     58 #define	PK_LDAP		4
     59 
     60 #define	CURMECH		mechs[mcount]
     61 #define	DESCREDPASSLEN	sizeof (des_block)
     62 
     63 static char	CRED_TABLE[] = "cred.org_dir";
     64 static char	PKMAP[] = "publickey.byname";
     65 static char	PKFILE[] = "/etc/publickey";
     66 #define	MAXHOSTNAMELEN	256
     67 
     68 #define	ROOTKEY_FILE		"/etc/.rootkey"
     69 #define	ROOTKEY_FILE_BACKUP	"/etc/.rootkey.bak"
     70 #define	MAXROOTKEY_LINE_LEN	4224	/* Good upto 16384-bit keys */
     71 #define	MAXROOTKEY_LEN		4096
     72 
     73 /* Should last up to 16384-bit keys */
     74 #define	MAXPKENTLEN	8500
     75 
     76 bool_t		makenew = TRUE;   /* Make new keys or reencrypt existing */
     77 bool_t		specmech = FALSE; /* Specific mechs requested */
     78 bool_t		force = FALSE;
     79 int		dest_service = 0; /* To which nameservice do we store key(s) */
     80 
     81 char		*program_name;
     82 
     83 mechanism_t	**mechs = NULL;   /* List of DH mechanisms */
     84 char		**plist = NULL;	  /* List of public key(s) */
     85 char		**slist = NULL;	  /* List of secret key(s) */
     86 char		**clist = NULL;   /* List of encrypted secret key(s) */
     87 int		numspecmech = 0;  /* Number of mechanisms specified */
     88 
     89 struct passwd	*pw = NULL;	  /* passwd entry of user */
     90 struct spwd	*spw = NULL;	  /* shadow entry of user */
     91 
     92 char		*netname = NULL;  /* RPC netname of user */
     93 char		local_domain[MAXNETNAMELEN + 1];
     94 char		*sec_domain = NULL;
     95 
     96 char		**rpc_pws = NULL; /* List of S-RPC passwords */
     97 int		rpc_pw_count = 0; /* Number of passwords entered by user */
     98 char		*login_pw = NULL; /* Unencrypted login password */
     99 char		short_login_pw[DESCREDPASSLEN + 1];
    100 /* Short S-RPC password, which has first 8 chars of login_pw */
    101 
    102 static int add_cred_obj(nis_object *, char *);
    103 static void cmp_passwd();
    104 static void encryptkeys();
    105 static void error_msg();
    106 static char *fgets_ignorenul();
    107 static void getpublics();
    108 static void getrpcpws();
    109 static void getsecrets();
    110 static void initkeylist(bool_t);
    111 static void keylogin(keylen_t, algtype_t);
    112 static void keylogin_des();
    113 static void makenewkeys();
    114 static int modify_cred_obj(nis_object *, char *);
    115 static void storekeys();
    116 static void usage();
    117 static void write_rootkey();
    118 
    119 extern nis_object *init_entry();
    120 extern int get_pk_source(char *);
    121 extern int localupdate(char *, char *, uint_t, char *);
    122 extern int xencrypt();
    123 extern int xencrypt_g();
    124 extern int __gen_dhkeys();
    125 extern int key_setnet();
    126 extern int key_setnet_g();
    127 extern int key_secretkey_is_set_g();
    128 extern int __getnetnamebyuid();
    129 extern int getdomainname();
    130 extern int ldap_update(char *, char *, char *, char *, char *);
    131 
    132 
    133 static void
    134 error_msg()
    135 {
    136 	if (sec_domain && *sec_domain &&
    137 	    strcasecmp(sec_domain, local_domain)) {
    138 		fprintf(stderr,
    139 "The system default domain '%s' is different from the Secure RPC\n\
    140 domain %s where the key is stored. \n", local_domain, sec_domain);
    141 		exit(1);
    142 	}
    143 }
    144 
    145 
    146 static void
    147 usage()
    148 {
    149 	fprintf(stderr, "usage: %s [-p] [-s ldap | nis | files] \n",
    150 	    program_name);
    151 	exit(1);
    152 }
    153 
    154 
    155 /* Encrypt secret key(s) with login_pw */
    156 static void
    157 encryptkeys()
    158 {
    159 	int	mcount, ccount = 0;
    160 
    161 	if (mechs) {
    162 		for (mcount = 0; CURMECH; mcount++) {
    163 			char		*crypt = NULL;
    164 
    165 			if (!xencrypt_g(slist[mcount], CURMECH->keylen,
    166 			    CURMECH->algtype, short_login_pw, netname,
    167 			    &crypt, TRUE)) {
    168 				/* Could not crypt key */
    169 				crypt = NULL;
    170 			} else
    171 				ccount++;
    172 			clist[mcount] = crypt;
    173 		}
    174 	} else {
    175 		char		*crypt = NULL;
    176 
    177 		if (!(crypt =
    178 		    (char *)malloc(HEXKEYBYTES + KEYCHECKSUMSIZE + 1))) {
    179 			fprintf(stderr, "%s: Malloc failure.\n", program_name);
    180 			exit(1);
    181 		}
    182 
    183 		(void) memcpy(crypt, slist[0], HEXKEYBYTES);
    184 		(void) memcpy(crypt + HEXKEYBYTES, slist[0], KEYCHECKSUMSIZE);
    185 		crypt[HEXKEYBYTES + KEYCHECKSUMSIZE] = 0;
    186 		xencrypt(crypt, short_login_pw);
    187 
    188 		clist[0] = crypt;
    189 		ccount++;
    190 	}
    191 
    192 	if (!ccount) {
    193 		fprintf(stderr, "%s: Could not encrypt any secret keys.\n",
    194 		    program_name);
    195 		exit(1);
    196 	}
    197 }
    198 
    199 
    200 /* Initialize the array of public, secret, and encrypted secret keys */
    201 static void
    202 initkeylist(bool_t nomech)
    203 {
    204 	int		mcount;
    205 
    206 	if (!nomech) {
    207 		assert(mechs && mechs[0]);
    208 		for (mcount = 0; CURMECH; mcount++)
    209 			;
    210 	} else
    211 		mcount = 1;
    212 
    213 	if (!(plist = (char **)malloc(sizeof (char *) * mcount))) {
    214 		fprintf(stderr, "%s: Malloc failure.\n", program_name);
    215 		exit(1);
    216 	}
    217 	if (!(slist = (char **)malloc(sizeof (char *) * mcount))) {
    218 		fprintf(stderr, "%s: Malloc failure.\n", program_name);
    219 		exit(1);
    220 	}
    221 	if (!(clist = (char **)malloc(sizeof (char *) * mcount))) {
    222 		fprintf(stderr, "%s: Malloc failure.\n", program_name);
    223 		exit(1);
    224 	}
    225 }
    226 
    227 
    228 /* Retrieve public key(s) */
    229 static void
    230 getpublics()
    231 {
    232 	int		mcount;
    233 	int		pcount = 0;
    234 
    235 	if (mechs) {
    236 		for (mcount = 0; CURMECH; mcount++) {
    237 			char		*public;
    238 			size_t		hexkeylen;
    239 
    240 			hexkeylen = ((CURMECH->keylen / 8) * 2) + 1;
    241 			if (!(public = (char *)malloc(hexkeylen))) {
    242 				fprintf(stderr, "%s: Malloc failure.\n",
    243 				    program_name);
    244 				exit(1);
    245 			}
    246 			if (!getpublickey_g(netname, CURMECH->keylen,
    247 			    CURMECH->algtype, public,
    248 			    hexkeylen)) {
    249 				/* Could not get public key */
    250 				fprintf(stderr,
    251 				    "Could not get %s public key.\n",
    252 				    VALID_ALIAS(CURMECH->alias) ?
    253 				    CURMECH->alias : "");
    254 				free(public);
    255 				public = NULL;
    256 			} else
    257 				pcount++;
    258 
    259 			plist[mcount] = public;
    260 		}
    261 	} else {
    262 		char		*public;
    263 
    264 		if (!(public = (char *)malloc(HEXKEYBYTES + 1))) {
    265 			fprintf(stderr, "%s: Malloc failure.\n", program_name);
    266 			exit(1);
    267 		}
    268 		if (!getpublickey(netname, public)) {
    269 			free(public);
    270 			public = NULL;
    271 		} else
    272 			pcount++;
    273 
    274 		plist[0] = public;
    275 	}
    276 
    277 	if (!pcount) {
    278 		fprintf(stderr, "%s: cannot get any public keys for %s.\n",
    279 		    program_name, pw->pw_name);
    280 		error_msg();
    281 		fprintf(stderr,
    282 	"Make sure that the public keys are stored in the domain %s.\n",
    283 		    local_domain);
    284 		exit(1);
    285 	}
    286 }
    287 
    288 
    289 /* Generate a new set of public/secret key pair(s) */
    290 static void
    291 makenewkeys()
    292 {
    293 	int		mcount;
    294 
    295 	if (mechs) {
    296 		for (mcount = 0; CURMECH; mcount++) {
    297 			char		*public, *secret;
    298 			size_t		hexkeylen;
    299 
    300 			if (slist[mcount])
    301 				free(slist[mcount]);
    302 
    303 			hexkeylen = ((CURMECH->keylen / 8) * 2) + 1;
    304 
    305 			if (!(public = malloc(hexkeylen))) {
    306 				fprintf(stderr, "%s: Malloc failure.\n",
    307 				    program_name);
    308 				exit(1);
    309 			}
    310 			if (!(secret = malloc(hexkeylen))) {
    311 				fprintf(stderr, "%s: Malloc failure.\n",
    312 				    program_name);
    313 				exit(1);
    314 			}
    315 
    316 			if (!(__gen_dhkeys_g(public, secret, CURMECH->keylen,
    317 			    CURMECH->algtype, short_login_pw))) {
    318 				/* Could not generate key pair */
    319 				fprintf(stderr,
    320 				"WARNING  Could not generate key pair %s\n",
    321 				    VALID_ALIAS(CURMECH->alias) ?
    322 				    CURMECH->alias : "");
    323 				free(public);
    324 				free(secret);
    325 				public = NULL;
    326 				secret = NULL;
    327 			}
    328 
    329 			plist[mcount] = public;
    330 			slist[mcount] = secret;
    331 		}
    332 	} else {
    333 		char		*public, *secret;
    334 		if (slist[0])
    335 			free(slist[0]);
    336 
    337 		if (!(public = malloc(HEXKEYBYTES + 1))) {
    338 			fprintf(stderr, "%s: Malloc failure.\n", program_name);
    339 			exit(1);
    340 		}
    341 		if (!(secret = malloc(HEXKEYBYTES + 1))) {
    342 			fprintf(stderr, "%s: Malloc failure.\n", program_name);
    343 			exit(1);
    344 		}
    345 
    346 		__gen_dhkeys(public, secret, short_login_pw);
    347 
    348 		plist[0] = public;
    349 		slist[0] = secret;
    350 	}
    351 }
    352 
    353 
    354 /*
    355  * Make sure that the entered Secure-RPC password(s) match the login
    356  * password
    357  */
    358 static void
    359 cmp_passwd()
    360 {
    361 	char	baseprompt[] = "Please enter the login password for";
    362 	char	prompt[BUFSIZ];
    363 	char	*en_login_pw = spw->sp_pwdp;
    364 	char	short_en_login_pw[DESCREDPASSLEN + 1];
    365 	char	*try_en_login_pw;
    366 	bool_t	pwmatch = FALSE;
    367 	int	done = 0, tries = 0, pcount;
    368 
    369 	snprintf(prompt, BUFSIZ, "%s %s:", baseprompt, pw->pw_name);
    370 
    371 	(void) strlcpy(short_en_login_pw, en_login_pw,
    372 	    sizeof (short_en_login_pw));
    373 
    374 	if (en_login_pw && (strlen(en_login_pw) != 0)) {
    375 		for (pcount = 0; pcount < rpc_pw_count; pcount++) {
    376 			char	*try_en_rpc_pw;
    377 
    378 		try_en_rpc_pw = crypt(rpc_pws[pcount], short_en_login_pw);
    379 			if (strcmp(try_en_rpc_pw, short_en_login_pw) == 0) {
    380 				login_pw = rpc_pws[pcount];
    381 				(void) strlcpy(short_login_pw, login_pw,
    382 				    sizeof (short_login_pw));
    383 				pwmatch = TRUE;
    384 				break;
    385 			}
    386 		}
    387 		if (!pwmatch) {
    388 			/* pw don't match */
    389 			while (!done) {
    390 				/* ask for the pw */
    391 				login_pw = getpassphrase(prompt);
    392 				(void) strlcpy(short_login_pw, login_pw,
    393 				    sizeof (short_login_pw));
    394 				if (login_pw && strlen(login_pw)) {
    395 					/* pw was not empty */
    396 					try_en_login_pw = crypt(login_pw,
    397 					    en_login_pw);
    398 					/* compare the pw's */
    399 					if (!(strcmp(try_en_login_pw,
    400 					    en_login_pw))) {
    401 						/* pw was correct */
    402 						return;
    403 					} else {
    404 						/* pw was wrong */
    405 						if (tries++) {
    406 							/* Sorry */
    407 							fprintf(stderr,
    408 							    "Sorry.\n");
    409 							exit(1);
    410 						} else {
    411 							/* Try again */
    412 							snprintf(prompt,
    413 							    BUFSIZ,
    414 							"Try again. %s %s:",
    415 							    baseprompt,
    416 							    pw->pw_name);
    417 						}
    418 					}
    419 				} else {
    420 					/* pw was empty */
    421 					if (tries++) {
    422 						/* Unchanged */
    423 						fprintf(stderr,
    424 					"%s: key-pair(s) unchanged for %s.\n",
    425 						    program_name,
    426 						    pw->pw_name);
    427 						exit(1);
    428 					} else {
    429 						/* Need a password */
    430 						snprintf(prompt, BUFSIZ,
    431 						"Need a password. %s %s:",
    432 						    baseprompt,
    433 						    pw->pw_name);
    434 					}
    435 				}
    436 			}
    437 		}
    438 		/* pw match */
    439 		return;
    440 	} else {
    441 		/* no pw found */
    442 		fprintf(stderr,
    443 		"%s: no passwd found for %s in the shadow passwd entry.\n",
    444 		    program_name, pw->pw_name);
    445 		exit(1);
    446 	}
    447 }
    448 
    449 
    450 /* Prompt the user for a Secure-RPC password and store it in a cache. */
    451 static void
    452 getrpcpws(char *flavor)
    453 {
    454 	char		*cur_pw = NULL;
    455 	char		prompt[BUFSIZ + 1];
    456 
    457 	if (flavor)
    458 		snprintf(prompt, BUFSIZ,
    459 		    "Please enter the %s Secure-RPC password for %s:",
    460 		    flavor, pw->pw_name);
    461 	else
    462 		snprintf(prompt, BUFSIZ,
    463 		    "Please enter the Secure-RPC password for %s:",
    464 		    pw->pw_name);
    465 
    466 	cur_pw = getpass(prompt);
    467 	if (!cur_pw) {
    468 		/* No changes */
    469 		fprintf(stderr, "%s: key-pair(s) unchanged for %s.\n",
    470 		    program_name, pw->pw_name);
    471 		exit(1);
    472 	}
    473 
    474 	rpc_pw_count++;
    475 	if (!(rpc_pws =
    476 	    (char **)realloc(rpc_pws, sizeof (char *) * rpc_pw_count))) {
    477 		fprintf(stderr, "%s: Realloc failure.\n", program_name);
    478 		exit(1);
    479 	}
    480 rpc_pws[rpc_pw_count - 1] = cur_pw;
    481 }
    482 
    483 
    484 /* Retrieve the secret key(s) for the user and attempt to decrypt them */
    485 static void
    486 getsecrets()
    487 {
    488 	int		mcount, scount = 0;
    489 	int		tries = 0;
    490 
    491 	getrpcpws(NULL);
    492 
    493 	if (mechs) {
    494 		for (mcount = 0; CURMECH; mcount++) {
    495 			char		*secret;
    496 			int		pcount;
    497 			size_t		hexkeylen;
    498 
    499 			hexkeylen = ((CURMECH->keylen / 8) * 2) + 1;
    500 			if (!(secret = (char *)calloc(hexkeylen,
    501 			    sizeof (char)))) {
    502 				fprintf(stderr, "%s: Malloc failure.\n",
    503 				    program_name);
    504 				exit(1);
    505 			}
    506 
    507 			for (pcount = 0; pcount < rpc_pw_count; pcount++) {
    508 				if (!getsecretkey_g(netname, CURMECH->keylen,
    509 				    CURMECH->algtype, secret,
    510 				    hexkeylen,
    511 				    rpc_pws[pcount]))
    512 					continue;
    513 
    514 				if (secret[0] == 0)
    515 					continue;
    516 				else
    517 					break;
    518 			}
    519 
    520 			tries = 0;
    521 		getsecrets_tryagain_g:
    522 			if (secret[0] == 0) {
    523 				if (!tries) {
    524 					/*
    525 					 * No existing pw can decrypt
    526 					 * secret key
    527 					 */
    528 					getrpcpws(CURMECH->alias);
    529 					if (!getsecretkey_g(netname,
    530 					    CURMECH->keylen,
    531 					    CURMECH->algtype,
    532 					    secret,
    533 					    hexkeylen,
    534 					    rpc_pws[pcount])) {
    535 						/*
    536 						 * Could not retreive
    537 						 * secret key, abort
    538 						 */
    539 						free(secret);
    540 						secret = NULL;
    541 						goto getsecrets_abort;
    542 					}
    543 
    544 					if (secret[0] == 0) {
    545 						/* Still no go, ask again */
    546 						free(rpc_pws[pcount]);
    547 						rpc_pw_count--;
    548 						tries++;
    549 						printf("Try again. ");
    550 						fflush(stdout);
    551 						goto getsecrets_tryagain_g;
    552 					} else
    553 						scount++;
    554 				} else {
    555 					fprintf(stderr,
    556 					"%s: key-pair unchanged for %s.\n",
    557 					    program_name, pw->pw_name);
    558 					exit(1);
    559 				}
    560 			} else
    561 				scount++;
    562 
    563 		getsecrets_abort:
    564 			slist[mcount] = secret;
    565 		}
    566 	} else {
    567 		char		*secret = NULL;
    568 
    569 		if (!(secret = (char *)malloc(HEXKEYBYTES + 1))) {
    570 			fprintf(stderr, "%s: Malloc failure.\n", program_name);
    571 			exit(1);
    572 		}
    573 	getsecrets_tryagain:
    574 		if (!getsecretkey(netname, secret, rpc_pws[0])) {
    575 			fprintf(stderr,
    576 			    "%s: could not get secret key for '%s'\n",
    577 			    program_name, netname);
    578 			exit(1);
    579 		}
    580 
    581 		if (secret[0] == 0) {
    582 			if (!tries) {
    583 				free(rpc_pws[0]);
    584 				rpc_pw_count = 0;
    585 				tries++;
    586 				printf("Try again. ");
    587 				fflush(stdout);
    588 				getrpcpws(NULL);
    589 				goto getsecrets_tryagain;
    590 			} else {
    591 				fprintf(stderr,
    592 				    "%s: key-pair unchanged for %s.\n",
    593 				    program_name, pw->pw_name);
    594 				exit(1);
    595 			}
    596 		}
    597 
    598 		slist[0] = secret;
    599 		return;
    600 	}
    601 
    602 	if (!scount) {
    603 		(void) fprintf(stderr,
    604 		"%s: could not get nor decrypt any secret keys for '%s'\n",
    605 		    program_name, netname);
    606 		error_msg();
    607 		exit(1);
    608 	}
    609 }
    610 
    611 
    612 /* Register AUTH_DES secret key with keyserv */
    613 static void
    614 keylogin_des()
    615 {
    616 	char			*secret = slist[0];
    617 	struct key_netstarg	netst;
    618 
    619 	/*
    620 	 * try to revoke the existing key/credentials, assuming
    621 	 * one exists.  this will effectively mark "stale" any
    622 	 * cached credientials...
    623 	 */
    624 	if (key_setsecret(secret) < 0) {
    625 		return;
    626 	}
    627 
    628 #ifdef NFS_AUTH
    629 	/*
    630 	 * it looks like a credential already existed, so try and
    631 	 * revoke any lingering Secure-NFS privledges.
    632 	 */
    633 
    634 	nra.authtype = AUTH_DES;
    635 	nra.uid = getuid();
    636 
    637 	if (_nfssys(NFS_REVAUTH, &nra) < 0)
    638 		perror("Warning: NFS credentials not destroyed");
    639 #endif /* NFS_AUTH */
    640 
    641 	(void) memcpy(netst.st_priv_key, secret, HEXKEYBYTES);
    642 
    643 	netst.st_pub_key[0] = '\0';
    644 	netst.st_netname = strdup(netname);
    645 
    646 	/* do actual key login */
    647 	if (key_setnet(&netst) < 0) {
    648 		fprintf(stderr, "Could not set %s's secret key\n", netname);
    649 		fprintf(stderr, "May be the keyserv is down?\n");
    650 	}
    651 }
    652 
    653 
    654 /* Register a secret key with the keyserv */
    655 static void
    656 keylogin(keylen_t keylen, algtype_t algtype)
    657 {
    658 	int	mcount;
    659 
    660 	if (mechs) {
    661 		for (mcount = 0; CURMECH; mcount++) {
    662 			if (keylen == CURMECH->keylen &&
    663 			    algtype == CURMECH->algtype) {
    664 				if (key_setnet_g(netname, slist[mcount],
    665 				    CURMECH->keylen,
    666 				    NULL, 0,
    667 				    CURMECH->algtype)
    668 				    < 0)
    669 					fprintf(stderr,
    670 					"Could not set %s's %s secret key\n",
    671 					    netname,
    672 					    VALID_ALIAS(CURMECH->alias) ?
    673 					    CURMECH->alias : "");
    674 			}
    675 		}
    676 	} else {
    677 		if (keylen == 192 && algtype == 0)
    678 			keylogin_des();
    679 	}
    680 }
    681 
    682 
    683 /*
    684  * fgets is "broken" in that if it reads a NUL character it will
    685  * always return EOF for all reads, even when there is data left in
    686  * the file.  This replacement can deal with NUL's in a calm, rational
    687  * manner.
    688  */
    689 static char *
    690 fgets_ignorenul(char *s, int n, FILE *stream)
    691 {
    692 	int fildes = fileno(stream);
    693 	int i = 0;
    694 	int rs = 0;
    695 	char c;
    696 
    697 	if (fildes < 0)
    698 		return (NULL);
    699 
    700 	while (i < n - 1) {
    701 		rs = read(fildes, &c, 1);
    702 		switch (rs) {
    703 		case 1:
    704 			break;
    705 		case 0:
    706 			/* EOF */
    707 			if (i > 0)
    708 				s[i] = '\0';
    709 			return (NULL);
    710 			break;
    711 		default:
    712 			return (NULL);
    713 		}
    714 		switch (c) {
    715 		case '\0':
    716 			break;
    717 		case '\n':
    718 			s[i] = c;
    719 			s[++i] = '\0';
    720 			return (s);
    721 		default:
    722 		if (c != '\0')
    723 			s[i++] = c;
    724 		}
    725 	}
    726 	s[i] = '\0';
    727 	return (s);
    728 }
    729 
    730 
    731 /* Write unencrypted secret key into root key file */
    732 static void
    733 write_rootkey(char *secret, char *flavor, keylen_t keylen, algtype_t algtype)
    734 {
    735 	char		line[MAXROOTKEY_LINE_LEN];
    736 	char		keyent[MAXROOTKEY_LEN];
    737 	algtype_t	atent;
    738 	int		rootfd, bakfd, hexkeybytes;
    739 	bool_t		lineone = TRUE;
    740 	bool_t		gotit = FALSE;
    741 	FILE		*rootfile, *bakfile;
    742 
    743 	unlink(ROOTKEY_FILE_BACKUP);
    744 	if ((rename(ROOTKEY_FILE, ROOTKEY_FILE_BACKUP)) < 0) {
    745 		if ((bakfd = creat(ROOTKEY_FILE_BACKUP, 0600)) < 0) {
    746 			perror("Could not create /etc/.rootkey.bak");
    747 			goto rootkey_err;
    748 		}
    749 		close(bakfd);
    750 	}
    751 
    752 	if ((rootfd = open(ROOTKEY_FILE, O_WRONLY+O_CREAT, 0600)) < 0) {
    753 		perror("Could not open /etc/.rootkey for writing");
    754 		fprintf(stderr,
    755 		    "Attempting to restore original /etc/.rootkey\n");
    756 		rename(ROOTKEY_FILE_BACKUP, ROOTKEY_FILE);
    757 		goto rootkey_err;
    758 	}
    759 	if (!(rootfile = fdopen(rootfd, "w"))) {
    760 		perror("Could not open /etc/.rootkey for writing");
    761 		fprintf(stderr,
    762 		    "Attempting to restore original /etc/.rootkey\n");
    763 		close(rootfd);
    764 		unlink(ROOTKEY_FILE);
    765 		rename(ROOTKEY_FILE_BACKUP, ROOTKEY_FILE);
    766 		goto rootkey_err;
    767 	}
    768 	if (!(bakfile = fopen(ROOTKEY_FILE_BACKUP, "r"))) {
    769 		perror("Could not open /etc/.rootkey.bak for reading");
    770 		fprintf(stderr,
    771 		    "Attempting to restore original /etc/.rootkey\n");
    772 		fclose(rootfile);
    773 		unlink(ROOTKEY_FILE);
    774 		rename(ROOTKEY_FILE_BACKUP, ROOTKEY_FILE);
    775 		goto rootkey_err;
    776 	}
    777 
    778 	hexkeybytes = ((keylen + 7) / 8) * 2;
    779 
    780 	while (fgets_ignorenul(line, MAXROOTKEY_LINE_LEN, bakfile)) {
    781 		if (sscanf(line, "%s %d", keyent, &atent) < 2) {
    782 			/*
    783 			 * No encryption algorithm found in the file
    784 			 * (atent) so default to DES.
    785 			 */
    786 			atent = AUTH_DES_ALGTYPE;
    787 		}
    788 		/*
    789 		 * 192-bit keys always go on the first line
    790 		 */
    791 		if (lineone) {
    792 			lineone = FALSE;
    793 			if (keylen == 192) {
    794 				gotit = TRUE;
    795 				fprintf(rootfile, "%s\n", secret);
    796 			} else
    797 				fprintf(rootfile, "%s", line);
    798 			fflush(rootfile);
    799 		} else {
    800 			if ((strlen(keyent) == hexkeybytes) &&
    801 			    (atent == algtype)) {
    802 				/*
    803 				 * Silently remove lines with the same
    804 				 * keylen/algtype
    805 				 */
    806 				if (gotit)
    807 					continue;
    808 				else
    809 					gotit = TRUE;
    810 
    811 				fprintf(rootfile, "%s %d\n", secret, algtype);
    812 			} else
    813 				fprintf(rootfile, "%s", line);
    814 			fflush(rootfile);
    815 		}
    816 	}
    817 
    818 	/* Append key to rootkey file */
    819 	if (!gotit) {
    820 		if (keylen == 192)
    821 			fprintf(rootfile, "%s\n", secret);
    822 		else {
    823 			if (lineone)
    824 				fprintf(rootfile, "\n");
    825 			fprintf(rootfile, "%s %d\n", secret, algtype);
    826 		}
    827 	}
    828 	fflush(rootfile);
    829 	fclose(rootfile);
    830 	fclose(bakfile);
    831 	unlink(ROOTKEY_FILE_BACKUP);
    832 	return;
    833 
    834 rootkey_err:
    835 	fprintf(stderr, "WARNING: Could not write %s key to /etc/.rootkey\n",
    836 	    flavor);
    837 }
    838 
    839 /* Store new key information in the specified name service */
    840 static void
    841 storekeys()
    842 {
    843 	int		mcount, ucount = 0;
    844 	char		*ypmaster, *ypdomain = NULL, pkent[MAXPKENTLEN];
    845 	nis_name	nis_princ;
    846 
    847 
    848 	/* Setup */
    849 	switch (dest_service) {
    850 	case PK_LDAP:
    851 		break;
    852 	case PK_YP:
    853 		yp_get_default_domain(&ypdomain);
    854 		if (yp_master(ypdomain, PKMAP, &ypmaster) != 0) {
    855 			fprintf(stderr,
    856 			"%s: cannot find master of NIS publickey database\n",
    857 			    program_name);
    858 			exit(1);
    859 		}
    860 		fprintf(stdout,
    861 		    "Sending key change request to %s ...\n", ypmaster);
    862 		break;
    863 	case PK_FILES:
    864 		if (geteuid() != 0) {
    865 			fprintf(stderr,
    866 		"%s: non-root users cannot change their key-pair in %s\n",
    867 			    program_name, PKFILE);
    868 			exit(1);
    869 		}
    870 		break;
    871 	default:
    872 		fprintf(stderr,
    873 		    "could not update; database %d unknown\n",
    874 		    dest_service);
    875 		exit(1);
    876 	}
    877 
    878 	if (mechs) {
    879 		for (mcount = 0; CURMECH; mcount++) {
    880 			char		authtype[MECH_MAXATNAME];
    881 
    882 			if (!plist[mcount] && !clist[mcount])
    883 				continue;
    884 
    885 			__nis_mechalias2authtype(CURMECH->alias, authtype,
    886 			    MECH_MAXATNAME);
    887 			if (!authtype) {
    888 				fprintf(stderr,
    889 				"Could not generate auth_type for %s.\n",
    890 				    CURMECH->alias);
    891 				continue;
    892 			}
    893 
    894 			snprintf(pkent, MAXPKENTLEN, "%s:%s:%d",
    895 			    plist[mcount], clist[mcount],
    896 			    CURMECH->algtype);
    897 
    898 			switch (dest_service) {
    899 			case PK_LDAP:
    900 				if (ldap_update(CURMECH->alias, netname,
    901 				    plist[mcount], clist[mcount],
    902 				    login_pw))
    903 					fprintf(stderr,
    904 			"%s: unable to update %s key in LDAP database\n",
    905 					    program_name, authtype);
    906 				else
    907 					ucount++;
    908 				break;
    909 
    910 			case PK_YP:
    911 				/* Should never get here. */
    912 				break;
    913 
    914 			case PK_FILES:
    915 				/* Should never get here. */
    916 				break;
    917 			}
    918 		}
    919 	} else {
    920 		int	status = 0;
    921 
    922 		assert(plist[0] && clist[0]);
    923 		snprintf(pkent, MAXPKENTLEN, "%s:%s", plist[0], clist[0]);
    924 
    925 		switch (dest_service) {
    926 		case PK_LDAP:
    927 			if (ldap_update("dh192-0", netname,
    928 			    plist[0], clist[0],
    929 			    login_pw)) {
    930 				fprintf(stderr,
    931 			"%s: unable to update %s key in LDAP database\n",
    932 				    program_name);
    933 				exit(1);
    934 			}
    935 			break;
    936 
    937 		case PK_YP:
    938 			if (status = yp_update(ypdomain, PKMAP,
    939 			    YPOP_STORE, netname,
    940 			    strlen(netname), pkent,
    941 			    strlen(pkent))) {
    942 				fprintf(stderr,
    943 				"%s: unable to update NIS database (%u): %s\n",
    944 				    program_name, status,
    945 				    yperr_string(status));
    946 				exit(1);
    947 			}
    948 			break;
    949 
    950 		case PK_FILES:
    951 			if (localupdate(netname, PKFILE, YPOP_STORE, pkent)) {
    952 				fprintf(stderr,
    953 			"%s: hence, unable to update publickey database\n",
    954 				    program_name);
    955 				exit(1);
    956 			}
    957 			break;
    958 
    959 		default:
    960 			/* Should never get here */
    961 			assert(0);
    962 		}
    963 		return;
    964 	}
    965 	if (!ucount) {
    966 		fprintf(stderr, "%s: unable to update any key-pairs for %s.\n",
    967 		    program_name, pw->pw_name);
    968 		exit(1);
    969 	}
    970 }
    971 
    972 void
    973 addmechtolist(char *mechtype)
    974 {
    975 	mechanism_t	**realmechlist;
    976 	int		i;
    977 
    978 	if (realmechlist = __nis_get_mechanisms(FALSE)) {
    979 		/* Match requested mech with list */
    980 		for (i = 0; realmechlist[i]; i++) {
    981 			if (realmechlist[i]->alias)
    982 				if (strcmp(realmechlist[i]->alias, mechtype)
    983 				    == 0) {
    984 					/*
    985 					 * Match, add it to the mechs.
    986 					 * Don't worry about qop or
    987 					 * secserv since they are not
    988 					 * used by chkey.
    989 					 */
    990 					numspecmech++;
    991 					if ((mechs =
    992 					    (mechanism_t **)realloc(mechs,
    993 					    sizeof (mechanism_t *) *
    994 					    (numspecmech + 1))) == NULL) {
    995 						perror("Can not change keys");
    996 						exit(1);
    997 					}
    998 
    999 					if ((mechs[numspecmech - 1] =
   1000 					    (mechanism_t *)malloc(
   1001 					    sizeof (mechanism_t))) == NULL) {
   1002 						perror("Can not change keys");
   1003 						exit(1);
   1004 					}
   1005 					if (realmechlist[i]->mechname)
   1006 					mechs[numspecmech - 1]->mechname =
   1007 					    strdup(realmechlist[i]->mechname);
   1008 					if (realmechlist[i]->alias)
   1009 					mechs[numspecmech - 1]->alias =
   1010 					    strdup(realmechlist[i]->alias);
   1011 					mechs[numspecmech - 1]->keylen =
   1012 					    realmechlist[i]->keylen;
   1013 					mechs[numspecmech - 1]->algtype =
   1014 					    realmechlist[i]->algtype;
   1015 					mechs[numspecmech] = NULL;
   1016 					__nis_release_mechanisms(realmechlist);
   1017 					return;
   1018 				}
   1019 		}
   1020 
   1021 		fprintf(stderr,
   1022 		"WARNING: Mechanism '%s' not configured, skipping...\n",
   1023 		    mechtype);
   1024 		__nis_release_mechanisms(realmechlist);
   1025 		return;
   1026 	}
   1027 	fprintf(stderr,
   1028 	"WARNING: Mechanism '%s' not configured, skipping...\n",
   1029 	    mechtype);
   1030 }
   1031 
   1032 
   1033 int
   1034 main(int argc, char **argv)
   1035 {
   1036 	int		c, mcount;
   1037 	uid_t		uid;
   1038 	uid_t		orig_euid;
   1039 	char		*service = NULL;
   1040 	program_name = argv[0];
   1041 
   1042 	mechs = __nis_get_mechanisms(FALSE);
   1043 
   1044 	while ((c = getopt(argc, argv, "fps:m:")) != -1) {
   1045 		switch (c) {
   1046 		case 'f':
   1047 			/*
   1048 			 * Not documented as of on1093.
   1049 			 * Temporarily supported
   1050 			 */
   1051 			force++;
   1052 			break;
   1053 		case 'p':
   1054 			makenew = FALSE;
   1055 			break;
   1056 		case 's':
   1057 			if (!service)
   1058 				service = strdup(optarg);
   1059 			else
   1060 				usage();
   1061 			break;
   1062 		case 'm':
   1063 			if (mechs && specmech == FALSE) {
   1064 				__nis_release_mechanisms(mechs);
   1065 				mechs = NULL;
   1066 			}
   1067 			specmech = TRUE;
   1068 			addmechtolist(optarg);
   1069 			break;
   1070 		default:
   1071 			usage();
   1072 		}
   1073 	}
   1074 
   1075 	if (optind < argc)
   1076 		usage();
   1077 
   1078 	dest_service = get_pk_source(service);
   1079 
   1080 	if (!(netname = malloc(MAXNETNAMELEN + 1))) {
   1081 		fprintf(stderr, "%s: Malloc failure.\n", program_name);
   1082 		exit(1);
   1083 	}
   1084 	if (!__getnetnamebyuid(netname, uid = getuid())) {
   1085 		fprintf(stderr, "%s: cannot generate netname for uid %d\n",
   1086 		    program_name, uid);
   1087 		exit(1);
   1088 	}
   1089 	sec_domain = strdup(strchr(netname, '@') + 1);
   1090 	getdomainname(local_domain, MAXNETNAMELEN);
   1091 
   1092 	if (makenew)
   1093 		fprintf(stdout, "Generating new key for '%s'.\n", netname);
   1094 	else
   1095 		fprintf(stdout, "Reencrypting key for '%s'.\n", netname);
   1096 
   1097 	if (mechs) {
   1098 		if (dest_service == PK_YP || dest_service == PK_FILES) {
   1099 			fprintf(stderr,
   1100 		"%s: can not add non-DES public keys to %s, skipping.\n",
   1101 			    program_name, service);
   1102 			__nis_release_mechanisms(mechs);
   1103 			mechs = NULL;
   1104 			initkeylist(TRUE);
   1105 		} else
   1106 			initkeylist(FALSE);
   1107 	} else
   1108 		initkeylist(TRUE);
   1109 
   1110 	uid = getuid();
   1111 	orig_euid = geteuid();
   1112 
   1113 	/* Get password information */
   1114 	if ((pw = getpwuid(uid)) == NULL) {
   1115 		fprintf(stderr,
   1116 		"%s: Can not find passwd information for %d.\n",
   1117 		    program_name, uid);
   1118 		exit(1);
   1119 	}
   1120 
   1121 	/* Set eUID to user */
   1122 	seteuid(uid);
   1123 
   1124 	/* Obtain a list of decrypted secret keys */
   1125 	getsecrets();
   1126 
   1127 	/* Keylogin user if not already done */
   1128 	if (mechs) {
   1129 		int mcount;
   1130 
   1131 		for (mcount = 0; CURMECH; mcount++) {
   1132 			keylen_t	keylen = CURMECH->keylen;
   1133 			algtype_t	algtype = CURMECH->algtype;
   1134 
   1135 			if (!key_secretkey_is_set_g(keylen, algtype) &&
   1136 			    slist[mcount]) {
   1137 				keylogin(CURMECH->keylen, CURMECH->algtype);
   1138 				if ((uid == 0) && (makenew == FALSE))
   1139 					write_rootkey(slist[mcount],
   1140 					    VALID_ALIAS(CURMECH->alias) ?
   1141 					    CURMECH->alias :
   1142 					    "",
   1143 					    keylen, algtype);
   1144 			}
   1145 		}
   1146 	} else {
   1147 		assert(slist[0]);
   1148 		if (!key_secretkey_is_set()) {
   1149 			keylogin_des();
   1150 			if ((uid == 0) && (makenew == FALSE))
   1151 				write_rootkey(slist[0], "des", 192, 0);
   1152 		}
   1153 	}
   1154 
   1155 	/* Set eUID back to root */
   1156 	(void) seteuid(orig_euid);
   1157 
   1158 	/*
   1159 	 * Call getspnam() after the keylogin has been done so we have
   1160 	 * the best chance of having read access to the encrypted pw.
   1161 	 *
   1162 	 * The eUID must be 0 for the getspnam() so the name service
   1163 	 * switch can handle the following eUID sensitive cases:
   1164 	 *
   1165 	 *	files/compat:	read /etc/shadow
   1166 	 *
   1167 	 */
   1168 	if ((spw = getspnam(pw->pw_name)) == 0) {
   1169 
   1170 		/* Set eUID back to user */
   1171 		(void) seteuid(uid);
   1172 
   1173 		(void) fprintf(stderr,
   1174 		"%s: cannot find shadow entry for %s.\n",
   1175 		    program_name, pw->pw_name);
   1176 		exit(1);
   1177 	}
   1178 
   1179 	/* Set eUID back to user */
   1180 	(void) seteuid(uid);
   1181 
   1182 	if (strcmp(spw->sp_pwdp, NOPWDRTR) == 0) {
   1183 		(void) fprintf(stderr,
   1184 		"%s: do not have read access to the passwd field for %s\n",
   1185 		    program_name, pw->pw_name);
   1186 		exit(1);
   1187 	}
   1188 
   1189 	/*
   1190 	 * force will be only supported for a while
   1191 	 * 	-- it is NOT documented as of s1093
   1192 	 */
   1193 	if (force) {
   1194 		char	*prompt = "Please enter New password:";
   1195 
   1196 		login_pw = getpassphrase(prompt);
   1197 		(void) strlcpy(short_login_pw, login_pw,
   1198 		    sizeof (short_login_pw));
   1199 		if (!login_pw || !(strlen(login_pw))) {
   1200 			fprintf(stderr, "%s: key-pair(s) unchanged for %s.\n",
   1201 			    program_name, pw->pw_name);
   1202 			exit(1);
   1203 		}
   1204 	} else {
   1205 		/*
   1206 		 * Reconsile rpc_pws and login_pw.
   1207 		 *
   1208 		 * This function will either return with login_pw == rpc_pw
   1209 		 * (and thus, the new pw to encrypt keys) or it will exit.
   1210 		 */
   1211 		cmp_passwd();
   1212 	}
   1213 
   1214 	if (makenew)
   1215 		makenewkeys();
   1216 	else
   1217 		getpublics();
   1218 
   1219 	encryptkeys();
   1220 
   1221 	storekeys();
   1222 
   1223 	if (makenew) {
   1224 		if (uid == 0) {
   1225 			if (mechs) {
   1226 				for (mcount = 0; CURMECH; mcount++) {
   1227 					if (!slist[mcount])
   1228 						continue;
   1229 					write_rootkey(slist[mcount],
   1230 					    CURMECH->alias,
   1231 					    CURMECH->keylen,
   1232 					    CURMECH->algtype);
   1233 				}
   1234 			} else {
   1235 				assert(slist[0]);
   1236 				write_rootkey(slist[0], "des", 192, 0);
   1237 			}
   1238 		}
   1239 		if (mechs) {
   1240 			for (mcount = 0; CURMECH; mcount++)
   1241 				keylogin(CURMECH->keylen,
   1242 				    CURMECH->algtype);
   1243 		} else
   1244 			keylogin_des();
   1245 	}
   1246 	return (0);
   1247 }
   1248