xref: /illumos-gate/usr/src/cmd/keyserv/chkey.c (revision 141040e8a310da49386b596573e5dde5580572ec)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved  	*/
29 
30 /*
31  * University Copyright- Copyright (c) 1982, 1986, 1988
32  * The Regents of the University of California
33  * All Rights Reserved
34  *
35  * University Acknowledgment- Portions of this document are derived from
36  * software developed by the University of California, Berkeley, and its
37  * contributors.
38  */
39 
40 #pragma ident	"%Z%%M%	%I%	%E% SMI"
41 
42 #include <assert.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <pwd.h>
47 #include <shadow.h>
48 #include <crypt.h>
49 #include <sys/types.h>
50 #include <unistd.h>
51 #include <rpc/rpc.h>
52 #include <rpc/key_prot.h>
53 #include <rpcsvc/nis.h>
54 #include <rpcsvc/nis_dhext.h>
55 #include <rpcsvc/ypclnt.h>
56 #include <nsswitch.h>
57 
58 #define	PK_FILES	1
59 #define	PK_YP		2
60 #define	PK_NISPLUS	3
61 #define	PK_LDAP		4
62 
63 #define	CURMECH		mechs[mcount]
64 
65 static char	CRED_TABLE[] = "cred.org_dir";
66 static char	PKMAP[] = "publickey.byname";
67 static char	PKFILE[] = "/etc/publickey";
68 #define	MAXHOSTNAMELEN	256
69 
70 #define	ROOTKEY_FILE		"/etc/.rootkey"
71 #define	ROOTKEY_FILE_BACKUP	"/etc/.rootkey.bak"
72 #define	MAXROOTKEY_LINE_LEN	4224	/* Good upto 16384-bit keys */
73 #define	MAXROOTKEY_LEN		4096
74 
75 /* Should last up to 16384-bit keys */
76 #define	MAXPKENTLEN	8500
77 
78 bool_t		makenew = TRUE;   /* Make new keys or reencrypt existing */
79 bool_t		specmech = FALSE; /* Specific mechs requested */
80 bool_t		force = FALSE;
81 int		dest_service = 0; /* To which nameservice do we store key(s) */
82 
83 char		*program_name;
84 
85 mechanism_t	**mechs = NULL;   /* List of DH mechanisms */
86 char		**plist = NULL;	  /* List of public key(s) */
87 char		**slist = NULL;	  /* List of secret key(s) */
88 char		**clist = NULL;   /* List of encrypted secret key(s) */
89 int		numspecmech = 0;  /* Number of mechanisms specified */
90 
91 struct passwd	*pw = NULL;	  /* passwd entry of user */
92 struct spwd	*spw = NULL;	  /* shadow entry of user */
93 
94 char		*netname = NULL;  /* RPC netname of user */
95 char		local_domain[MAXNETNAMELEN + 1];
96 char		*sec_domain = NULL;
97 
98 char		**rpc_pws = NULL; /* List of S-RPC passwords */
99 int		rpc_pw_count = 0; /* Number of passwords entered by user */
100 char		*login_pw = NULL; /* Unencrypted login password */
101 
102 static int add_cred_obj(nis_object *, char *);
103 static nis_error auth_exists(char *, char *, char *, char *);
104 static void cmp_passwd();
105 static nis_error cred_exists(const char *, const char *, const char *);
106 static void encryptkeys();
107 static void error_msg();
108 static char *fgets_ignorenul();
109 static void getpublics();
110 static void getrpcpws();
111 static void getsecrets();
112 static void initkeylist(bool_t);
113 static void keylogin(keylen_t, algtype_t);
114 static void keylogin_des();
115 static void makenewkeys();
116 static int modify_cred_obj(nis_object *, char *);
117 static int nisplus_update(nis_name, char *, char *, char *);
118 static int sanity_checks(char *, char *, char *);
119 static void storekeys();
120 static void usage();
121 static void write_rootkey();
122 
123 extern char *get_nisplus_principal(char *, uid_t);
124 extern nis_object *init_entry();
125 extern int get_pk_source(char *);
126 extern int localupdate(char *, char *, uint_t, char *);
127 extern int xencrypt();
128 extern int xencrypt_g();
129 extern int __gen_dhkeys();
130 extern int key_setnet();
131 extern int key_setnet_g();
132 extern int key_secretkey_is_set_g();
133 extern int __getnetnamebyuid();
134 extern int getdomainname();
135 extern int ldap_update(char *, char *, char *, char *, char *);
136 
137 
138 static void
139 error_msg()
140 {
141 	if (sec_domain && *sec_domain &&
142 	    strcasecmp(sec_domain, local_domain)) {
143 		fprintf(stderr,
144 "The system default domain '%s' is different from the Secure RPC\n\
145 domain %s where the key is stored.  The Secure RPC domainname is\n\
146 defined by the directory object stored in the /var/nis/NIS_COLD_START file.\n\
147 If you need to change this Secure RPC domainname, please use the nisinit(1M)\n\
148 command with the `-k` option.\n", local_domain, sec_domain);
149 		exit(1);
150 	}
151 }
152 
153 
154 static void
155 usage()
156 {
157 	fprintf(stderr, "usage: %s [-p] [-s ldap | nisplus | nis | files] \n",
158 		program_name);
159 	exit(1);
160 }
161 
162 
163 /* Encrypt secret key(s) with login_pw */
164 static void
165 encryptkeys()
166 {
167 	int	mcount, ccount = 0;
168 
169 	if (mechs) {
170 		for (mcount = 0; CURMECH; mcount++) {
171 			char		*crypt = NULL;
172 
173 			if (!xencrypt_g(slist[mcount], CURMECH->keylen,
174 					CURMECH->algtype, login_pw, netname,
175 					&crypt, TRUE)) {
176 				/* Could not crypt key */
177 				crypt = NULL;
178 			} else
179 				ccount++;
180 			clist[mcount] = crypt;
181 		}
182 	} else {
183 		char		*crypt = NULL;
184 
185 		if (!(crypt =
186 			(char *)malloc(HEXKEYBYTES + KEYCHECKSUMSIZE + 1))) {
187 			fprintf(stderr, "%s: Malloc failure.\n", program_name);
188 			exit(1);
189 		}
190 
191 		memcpy(crypt, slist[0], HEXKEYBYTES);
192 		memcpy(crypt + HEXKEYBYTES, slist[0], KEYCHECKSUMSIZE);
193 		crypt[HEXKEYBYTES + KEYCHECKSUMSIZE] = 0;
194 		xencrypt(crypt, login_pw);
195 
196 		clist[0] = crypt;
197 		ccount++;
198 	}
199 
200 	if (!ccount) {
201 		fprintf(stderr, "%s: Could not encrypt any secret keys.\n",
202 			program_name);
203 		exit(1);
204 	}
205 }
206 
207 
208 /* Initialize the array of public, secret, and encrypted secret keys */
209 static void
210 initkeylist(bool_t nomech)
211 {
212 	int		mcount;
213 
214 	if (!nomech) {
215 		assert(mechs && mechs[0]);
216 		for (mcount = 0; CURMECH; mcount++);
217 	} else
218 		mcount = 1;
219 
220 	if (!(plist = (char **)malloc(sizeof (char *) * mcount))) {
221 		fprintf(stderr, "%s: Malloc failure.\n", program_name);
222 		exit(1);
223 	}
224 	if (!(slist = (char **)malloc(sizeof (char *) * mcount))) {
225 		fprintf(stderr, "%s: Malloc failure.\n", program_name);
226 		exit(1);
227 	}
228 	if (!(clist = (char **)malloc(sizeof (char *) * mcount))) {
229 		fprintf(stderr, "%s: Malloc failure.\n", program_name);
230 		exit(1);
231 	}
232 }
233 
234 
235 /* Retrieve public key(s) */
236 static void
237 getpublics()
238 {
239 	int		mcount;
240 	int		pcount = 0;
241 
242 	if (mechs) {
243 		for (mcount = 0; CURMECH; mcount++) {
244 			char		*public;
245 			size_t		hexkeylen;
246 
247 			hexkeylen = ((CURMECH->keylen / 8) * 2) + 1;
248 			if (!(public = (char *)malloc(hexkeylen))) {
249 				fprintf(stderr, "%s: Malloc failure.\n",
250 					program_name);
251 				exit(1);
252 			}
253 			if (!getpublickey_g(netname, CURMECH->keylen,
254 					    CURMECH->algtype, public,
255 					    hexkeylen)) {
256 				/* Could not get public key */
257 				fprintf(stderr,
258 					"Could not get %s public key.\n",
259 					VALID_ALIAS(CURMECH->alias) ?
260 					CURMECH->alias : "");
261 				free(public);
262 				public = NULL;
263 			} else
264 				pcount++;
265 
266 			plist[mcount] = public;
267 		}
268 	} else {
269 		char		*public;
270 
271 		if (!(public = (char *)malloc(HEXKEYBYTES + 1))) {
272 			fprintf(stderr, "%s: Malloc failure.\n", program_name);
273 			exit(1);
274 		}
275 		if (!getpublickey(netname, public)) {
276 			free(public);
277 			public = NULL;
278 		} else
279 			pcount++;
280 
281 		plist[0] = public;
282 	}
283 
284 	if (!pcount) {
285 		fprintf(stderr, "%s: cannot get any public keys for %s.\n",
286 			program_name, pw->pw_name);
287 		error_msg();
288 		fprintf(stderr,
289 	"Make sure that the public keys are stored in the domain %s.\n",
290 			local_domain);
291 		exit(1);
292 	}
293 }
294 
295 
296 /* Generate a new set of public/secret key pair(s) */
297 static void
298 makenewkeys()
299 {
300 	int		mcount;
301 
302 	if (mechs) {
303 		for (mcount = 0; CURMECH; mcount++) {
304 			char		*public, *secret;
305 			size_t		hexkeylen;
306 
307 			if (slist[mcount])
308 				free(slist[mcount]);
309 
310 			hexkeylen = ((CURMECH->keylen / 8) * 2) + 1;
311 
312 			if (!(public = malloc(hexkeylen))) {
313 				fprintf(stderr, "%s: Malloc failure.\n",
314 					program_name);
315 				exit(1);
316 			}
317 			if (!(secret = malloc(hexkeylen))) {
318 				fprintf(stderr, "%s: Malloc failure.\n",
319 					program_name);
320 				exit(1);
321 			}
322 
323 			if (!(__gen_dhkeys_g(public, secret, CURMECH->keylen,
324 					CURMECH->algtype, login_pw))) {
325 				/* Could not generate key pair */
326 				fprintf(stderr,
327 				"WARNING  Could not generate key pair %s\n",
328 					VALID_ALIAS(CURMECH->alias) ?
329 					CURMECH->alias : "");
330 				free(public);
331 				free(secret);
332 				public = NULL;
333 				secret = NULL;
334 			}
335 
336 			plist[mcount] = public;
337 			slist[mcount] = secret;
338 		}
339 	} else {
340 		char		*public, *secret;
341 		if (slist[0])
342 			free(slist[0]);
343 
344 		if (!(public = malloc(HEXKEYBYTES + 1))) {
345 			fprintf(stderr, "%s: Malloc failure.\n", program_name);
346 			exit(1);
347 		}
348 		if (!(secret = malloc(HEXKEYBYTES + 1))) {
349 			fprintf(stderr, "%s: Malloc failure.\n", program_name);
350 			exit(1);
351 		}
352 
353 		__gen_dhkeys(public, secret, login_pw);
354 
355 		plist[0] = public;
356 		slist[0] = secret;
357 	}
358 }
359 
360 
361 /*
362  * Make sure that the entered Secure-RPC password(s) match the login
363  * password
364  */
365 static void
366 cmp_passwd()
367 {
368 	char	baseprompt[] = "Please enter the login password for";
369 	char	prompt[BUFSIZ];
370 	char	*en_login_pw = spw->sp_pwdp;
371 	char	*try_en_login_pw;
372 	bool_t	pwmatch = FALSE;
373 	int	done = 0, tries = 0, pcount;
374 
375 	snprintf(prompt, BUFSIZ, "%s %s:", baseprompt, pw->pw_name);
376 
377 	if (en_login_pw && (strlen(en_login_pw) != 0)) {
378 		for (pcount = 0; pcount < rpc_pw_count; pcount++) {
379 			char	*try_en_rpc_pw;
380 
381 			try_en_rpc_pw = crypt(rpc_pws[pcount], en_login_pw);
382 			if (strcmp(try_en_rpc_pw, en_login_pw) == 0) {
383 				login_pw = rpc_pws[pcount];
384 				pwmatch = TRUE;
385 				break;
386 			}
387 		}
388 		if (!pwmatch) {
389 			/* pw don't match */
390 			while (!done) {
391 				/* ask for the pw */
392 				login_pw = getpass(prompt);
393 				if (login_pw && strlen(login_pw)) {
394 					/* pw was not empty */
395 					try_en_login_pw = crypt(login_pw,
396 								en_login_pw);
397 					/* compare the pw's */
398 					if (!(strcmp(try_en_login_pw,
399 							en_login_pw))) {
400 						/* pw was correct */
401 						return;
402 					} else {
403 						/* pw was wrong */
404 						if (tries++) {
405 							/* Sorry */
406 							fprintf(stderr,
407 								"Sorry.\n");
408 							exit(1);
409 						} else {
410 							/* Try again */
411 							snprintf(prompt,
412 									BUFSIZ,
413 							"Try again. %s %s:",
414 								baseprompt,
415 								pw->pw_name);
416 						}
417 					}
418 				} else {
419 					/* pw was empty */
420 					if (tries++) {
421 						/* Unchanged */
422 						fprintf(stderr,
423 					"%s: key-pair(s) unchanged for %s.\n",
424 							program_name,
425 							pw->pw_name);
426 						exit(1);
427 					} else {
428 						/* Need a password */
429 						snprintf(prompt, BUFSIZ,
430 						"Need a password. %s %s:",
431 								baseprompt,
432 								pw->pw_name);
433 					}
434 				}
435 			}
436 		}
437 		/* pw match */
438 		return;
439 	} else {
440 		/* no pw found */
441 		fprintf(stderr,
442 		"%s: no passwd found for %s in the shadow passwd entry.\n",
443 			program_name, pw->pw_name);
444 		exit(1);
445 	}
446 }
447 
448 
449 /* Prompt the user for a Secure-RPC password and store it in a cache. */
450 static void
451 getrpcpws(char *flavor)
452 {
453 	char		*cur_pw = NULL;
454 	char		prompt[BUFSIZ + 1];
455 
456 	if (flavor)
457 		snprintf(prompt, BUFSIZ,
458 			"Please enter the %s Secure-RPC password for %s:",
459 			flavor, pw->pw_name);
460 	else
461 		snprintf(prompt, BUFSIZ,
462 				"Please enter the Secure-RPC password for %s:",
463 				pw->pw_name);
464 
465 	cur_pw = getpass(prompt);
466 	if (!cur_pw) {
467 		/* No changes */
468 		fprintf(stderr, "%s: key-pair(s) unchanged for %s.\n",
469 			program_name, pw->pw_name);
470 		exit(1);
471 	}
472 
473 	rpc_pw_count++;
474 	if (!(rpc_pws =
475 		(char **)realloc(rpc_pws, sizeof (char *) * rpc_pw_count))) {
476 		fprintf(stderr, "%s: Realloc failure.\n", program_name);
477 		exit(1);
478 	}
479 rpc_pws[rpc_pw_count - 1] = cur_pw;
480 }
481 
482 
483 /* Retrieve the secret key(s) for the user and attempt to decrypt them */
484 static void
485 getsecrets()
486 {
487 	int		mcount, scount = 0;
488 	int		tries = 0;
489 
490 	getrpcpws(NULL);
491 
492 	if (mechs) {
493 		for (mcount = 0; CURMECH; mcount++) {
494 			char		*secret;
495 			int		pcount;
496 			size_t		hexkeylen;
497 
498 			hexkeylen = ((CURMECH->keylen / 8) * 2) + 1;
499 			if (!(secret = (char *)calloc(hexkeylen,
500 							sizeof (char)))) {
501 				fprintf(stderr, "%s: Malloc failure.\n",
502 					program_name);
503 				exit(1);
504 			}
505 
506 			for (pcount = 0; pcount < rpc_pw_count; pcount++) {
507 				if (!getsecretkey_g(netname, CURMECH->keylen,
508 						    CURMECH->algtype, secret,
509 						    hexkeylen,
510 						    rpc_pws[pcount]))
511 					continue;
512 
513 				if (secret[0] == 0)
514 					continue;
515 				else
516 					break;
517 			}
518 
519 			tries = 0;
520 		getsecrets_tryagain_g:
521 			if (secret[0] == 0) {
522 				if (!tries) {
523 					/*
524 					 * No existing pw can decrypt
525 					 * secret key
526 					 */
527 					getrpcpws(CURMECH->alias);
528 					if (!getsecretkey_g(netname,
529 							    CURMECH->keylen,
530 							    CURMECH->algtype,
531 							    secret,
532 							    hexkeylen,
533 							    rpc_pws[pcount])) {
534 						/*
535 						 * Could not retreive
536 						 * secret key, abort
537 						 */
538 						free(secret);
539 						secret = NULL;
540 						goto getsecrets_abort;
541 					}
542 
543 					if (secret[0] == 0) {
544 						/* Still no go, ask again */
545 						free(rpc_pws[pcount]);
546 						rpc_pw_count--;
547 						tries++;
548 						printf("Try again. ");
549 						fflush(stdout);
550 						goto getsecrets_tryagain_g;
551 					} else
552 						scount++;
553 				} else {
554 					fprintf(stderr,
555 					"%s: key-pair unchanged for %s.\n",
556 						program_name, pw->pw_name);
557 					exit(1);
558 				}
559 			} else
560 				scount++;
561 
562 		getsecrets_abort:
563 			slist[mcount] = secret;
564 		}
565 	} else {
566 		char		*secret = NULL;
567 
568 		if (!(secret = (char *)malloc(HEXKEYBYTES + 1))) {
569 			fprintf(stderr, "%s: Malloc failure.\n", program_name);
570 			exit(1);
571 		}
572 	getsecrets_tryagain:
573 		if (!getsecretkey(netname, secret, rpc_pws[0])) {
574 			fprintf(stderr,
575 				"%s: could not get secret key for '%s'\n",
576 				program_name, netname);
577 			exit(1);
578 		}
579 
580 		if (secret[0] == 0) {
581 			if (!tries) {
582 				free(rpc_pws[0]);
583 				rpc_pw_count = 0;
584 				tries++;
585 				printf("Try again. ");
586 				fflush(stdout);
587 				getrpcpws(NULL);
588 				goto getsecrets_tryagain;
589 			} else {
590 				fprintf(stderr,
591 					"%s: key-pair unchanged for %s.\n",
592 					program_name, pw->pw_name);
593 				exit(1);
594 			}
595 		}
596 
597 		slist[0] = secret;
598 		return;
599 	}
600 
601 	if (!scount) {
602 		(void) fprintf(stderr,
603 		"%s: could not get nor decrypt any secret keys for '%s'\n",
604 					program_name, netname);
605 		error_msg();
606 		exit(1);
607 	}
608 }
609 
610 
611 /* Register AUTH_DES secret key with keyserv */
612 static void
613 keylogin_des()
614 {
615 	char			*secret = slist[0];
616 	struct key_netstarg	netst;
617 
618 	/*
619 	 * try to revoke the existing key/credentials, assuming
620 	 * one exists.  this will effectively mark "stale" any
621 	 * cached credientials...
622 	 */
623 	if (key_setsecret(secret) < 0) {
624 		return;
625 	}
626 
627 #ifdef NFS_AUTH
628 	/*
629 	 * it looks like a credential already existed, so try and
630 	 * revoke any lingering Secure-NFS privledges.
631 	 */
632 
633 	nra.authtype = AUTH_DES;
634 	nra.uid = getuid();
635 
636 	if (_nfssys(NFS_REVAUTH, &nra) < 0)
637 		perror("Warning: NFS credentials not destroyed");
638 #endif /* NFS_AUTH */
639 
640 	memcpy(netst.st_priv_key, secret, HEXKEYBYTES);
641 
642 	netst.st_pub_key[0] = '\0';
643 	netst.st_netname = strdup(netname);
644 
645 	/* do actual key login */
646 	if (key_setnet(&netst) < 0) {
647 		fprintf(stderr, "Could not set %s's secret key\n", netname);
648 		fprintf(stderr, "May be the keyserv is down?\n");
649 	}
650 }
651 
652 
653 /* Register a secret key with the keyserv */
654 static void
655 keylogin(keylen_t keylen, algtype_t algtype)
656 {
657 	int	mcount;
658 
659 	if (mechs) {
660 		for (mcount = 0; CURMECH; mcount++) {
661 			if (keylen == CURMECH->keylen &&
662 			    algtype == CURMECH->algtype) {
663 				if (key_setnet_g(netname, slist[mcount],
664 							CURMECH->keylen,
665 							NULL, 0,
666 							CURMECH->algtype)
667 				    < 0)
668 					fprintf(stderr,
669 					"Could not set %s's %s secret key\n",
670 						netname,
671 					VALID_ALIAS(CURMECH->alias) ?
672 						CURMECH->alias : "");
673 			}
674 		}
675 	} else {
676 		if (keylen == 192 && algtype == 0)
677 			keylogin_des();
678 	}
679 }
680 
681 
682 /*
683  * fgets is "broken" in that if it reads a NUL character it will
684  * always return EOF for all reads, even when there is data left in
685  * the file.  This replacement can deal with NUL's in a calm, rational
686  * manner.
687  */
688 static char *
689 fgets_ignorenul(char *s, int n, FILE *stream)
690 {
691 	int fildes = fileno(stream);
692 	int i = 0;
693 	int rs = 0;
694 	char c;
695 
696 	if (fildes < 0)
697 		return (NULL);
698 
699 	while (i < n - 1) {
700 		rs = read(fildes, &c, 1);
701 		switch (rs) {
702 		case 1:
703 			break;
704 		case 0:
705 			/* EOF */
706 			if (i > 0)
707 				s[i] = '\0';
708 			return (NULL);
709 			break;
710 		default:
711 			return (NULL);
712 		}
713 		switch (c) {
714 		case '\0':
715 			break;
716 		case '\n':
717 			s[i] = c;
718 			s[++i] = '\0';
719 			return (s);
720 		default:
721 		if (c != '\0')
722 			s[i++] = c;
723 		}
724 	}
725 	s[i] = '\0';
726 	return (s);
727 }
728 
729 
730 /* Write unencrypted secret key into root key file */
731 static void
732 write_rootkey(char *secret, char *flavor, keylen_t keylen, algtype_t algtype)
733 {
734 	char		line[MAXROOTKEY_LINE_LEN];
735 	char		keyent[MAXROOTKEY_LEN];
736 	algtype_t	atent;
737 	int		rootfd, bakfd, hexkeybytes;
738 	bool_t		lineone = TRUE;
739 	bool_t		gotit = FALSE;
740 	FILE		*rootfile, *bakfile;
741 
742 	unlink(ROOTKEY_FILE_BACKUP);
743 	if ((rename(ROOTKEY_FILE, ROOTKEY_FILE_BACKUP)) < 0) {
744 		if ((bakfd = creat(ROOTKEY_FILE_BACKUP, 0600)) < 0) {
745 			perror("Could not create /etc/.rootkey.bak");
746 			goto rootkey_err;
747 		}
748 		close(bakfd);
749 	}
750 
751 	if ((rootfd = open(ROOTKEY_FILE, O_WRONLY+O_CREAT, 0600)) < 0) {
752 		perror("Could not open /etc/.rootkey for writing");
753 		fprintf(stderr,
754 			"Attempting to restore original /etc/.rootkey\n");
755 		rename(ROOTKEY_FILE_BACKUP, ROOTKEY_FILE);
756 		goto rootkey_err;
757 	}
758 	if (!(rootfile = fdopen(rootfd, "w"))) {
759 		perror("Could not open /etc/.rootkey for writing");
760 		fprintf(stderr,
761 			"Attempting to restore original /etc/.rootkey\n");
762 		close(rootfd);
763 		unlink(ROOTKEY_FILE);
764 		rename(ROOTKEY_FILE_BACKUP, ROOTKEY_FILE);
765 		goto rootkey_err;
766 	}
767 	if (!(bakfile = fopen(ROOTKEY_FILE_BACKUP, "r"))) {
768 		perror("Could not open /etc/.rootkey.bak for reading");
769 		fprintf(stderr,
770 			"Attempting to restore original /etc/.rootkey\n");
771 		fclose(rootfile);
772 		unlink(ROOTKEY_FILE);
773 		rename(ROOTKEY_FILE_BACKUP, ROOTKEY_FILE);
774 		goto rootkey_err;
775 	}
776 
777 	hexkeybytes = ((keylen + 7) / 8) * 2;
778 
779 	while (fgets_ignorenul(line, MAXROOTKEY_LINE_LEN, bakfile)) {
780 		sscanf(line, "%s %d", keyent, &atent);
781 		/*
782 		 * 192-bit keys always go on the first line
783 		 */
784 		if (lineone) {
785 			lineone = FALSE;
786 			if (keylen == 192) {
787 				gotit = TRUE;
788 				fprintf(rootfile, "%s\n", secret);
789 			} else
790 				fprintf(rootfile, "%s", line);
791 			fflush(rootfile);
792 		} else {
793 			if ((strlen(keyent) == hexkeybytes) &&
794 			    (atent == algtype)) {
795 				/*
796 				 * Silently remove lines with the same
797 				 * keylen/algtype
798 				 */
799 				if (gotit)
800 					continue;
801 				else
802 					gotit = TRUE;
803 
804 				fprintf(rootfile, "%s %d\n", secret, algtype);
805 			} else
806 				fprintf(rootfile, "%s", line);
807 			fflush(rootfile);
808 		}
809 	}
810 
811 	/* Append key to rootkey file */
812 	if (!gotit) {
813 		if (keylen == 192)
814 			fprintf(rootfile, "%s\n", secret);
815 		else {
816 			if (lineone)
817 				fprintf(rootfile, "\n");
818 			fprintf(rootfile, "%s %d\n", secret, algtype);
819 		}
820 	}
821 	fflush(rootfile);
822 	fclose(rootfile);
823 	fclose(bakfile);
824 	unlink(ROOTKEY_FILE_BACKUP);
825 	return;
826 
827 rootkey_err:
828 	fprintf(stderr, "WARNING: Could not write %s key to /etc/.rootkey\n",
829 		flavor);
830 }
831 
832 
833 /* Returns 0 if check fails; 1 if successful. */
834 static int
835 sanity_checks(char *nis_princ, char *domain, char *authtype)
836 {
837 	char	netdomainaux[MAXHOSTNAMELEN+1];
838 	char	*princdomain, *netdomain;
839 	int	len;
840 
841 	/* Sanity check 0. Do we have a nis+ principal name to work with? */
842 	if (nis_princ == NULL) {
843 		(void) fprintf(stderr,
844 		"%s: you must create a \"LOCAL\" credential for '%s' first.\n",
845 			program_name, netname);
846 		(void) fprintf(stderr, "\tSee nisaddcred(1).\n");
847 		return (0);
848 	}
849 
850 	/* Sanity check 0.5.  NIS+ principal names must be dotted. */
851 	len = strlen(nis_princ);
852 	if (nis_princ[len-1] != '.') {
853 		(void) fprintf(stderr,
854 		"%s: invalid principal name: '%s' (forgot ending dot?).\n",
855 			program_name, nis_princ);
856 		return (0);
857 	}
858 
859 	/* Sanity check 1.  We only deal with one type of netnames. */
860 	if (strncmp(netname, "unix", 4) != 0) {
861 		(void) fprintf(stderr,
862 			"%s: unrecognized netname type: '%s'.\n",
863 			program_name, netname);
864 		return (0);
865 	}
866 
867 	/* Sanity check 2.  Should only add DES cred in home domain. */
868 	princdomain = nis_domain_of(nis_princ);
869 	if (strcasecmp(princdomain, domain) != 0) {
870 		(void) fprintf(stderr,
871 "%s: domain of principal '%s' does not match destination domain '%s'.\n",
872 			program_name, nis_princ, domain);
873 		(void) fprintf(stderr,
874 	"Should only add DES credential of principal in its home domain\n");
875 		return (0);
876 	}
877 
878 	/*
879 	 * Sanity check 3:  Make sure netname's domain same as principal's
880 	 * and don't have extraneous dot at the end.
881 	 */
882 	netdomain = (char *)strchr(netname, '@');
883 	if (! netdomain || netname[strlen(netname)-1] == '.') {
884 		(void) fprintf(stderr, "%s: invalid netname: '%s'. \n",
885 			program_name, netname);
886 		return (0);
887 	}
888 	netdomain++; /* skip '@' */
889 
890 	if (strlcpy(netdomainaux, netdomain, sizeof (netdomainaux)) >=
891 	    sizeof (netdomainaux)) {
892 		(void) fprintf(stderr, "%s: net domain name %s is too long\n",
893 			    program_name, netdomain);
894 		return (0);
895 	}
896 
897 	if (netdomainaux[strlen(netdomainaux) - 1] != '.') {
898 		if (strlcat(netdomainaux, ".", sizeof (netdomainaux)) >=
899 		    sizeof (netdomainaux)) {
900 			(void) fprintf(stderr,
901 				    "%s: net domain name %s is too long\n",
902 				    program_name, netdomainaux);
903 			return (0);
904 		}
905 	}
906 
907 	if (strcasecmp(princdomain, netdomainaux) != 0) {
908 		(void) fprintf(stderr,
909 	"%s: domain of netname %s should be same as that of principal %s\n",
910 			program_name, netname, nis_princ);
911 		return (0);
912 	}
913 
914 	/* Another principal owns same credentials? (exits if that happens) */
915 	(void) auth_exists(nis_princ, netname, authtype, domain);
916 
917 	return (1); /* all passed */
918 }
919 
920 
921 /* Store new key information in the specified name service */
922 static void
923 storekeys()
924 {
925 	int		mcount, ucount = 0;
926 	char		*ypmaster, *ypdomain = NULL, pkent[MAXPKENTLEN];
927 	nis_name	nis_princ;
928 
929 
930 	/* Setup */
931 	switch (dest_service) {
932 	case PK_LDAP:
933 		break;
934 	case PK_NISPLUS:
935 		nis_princ = get_nisplus_principal(nis_local_directory(),
936 							geteuid());
937 		break;
938 	case PK_YP:
939 		yp_get_default_domain(&ypdomain);
940 		if (yp_master(ypdomain, PKMAP, &ypmaster) != 0) {
941 			fprintf(stderr,
942 			"%s: cannot find master of NIS publickey database\n",
943 				program_name);
944 			exit(1);
945 		}
946 		fprintf(stdout,
947 			"Sending key change request to %s ...\n", ypmaster);
948 		break;
949 	case PK_FILES:
950 		if (geteuid() != 0) {
951 			fprintf(stderr,
952 		"%s: non-root users cannot change their key-pair in %s\n",
953 				program_name, PKFILE);
954 			exit(1);
955 		}
956 		break;
957 	default:
958 		fprintf(stderr,
959 			"could not update; database %d unknown\n",
960 			dest_service);
961 		exit(1);
962 	}
963 
964 	if (mechs) {
965 		for (mcount = 0; CURMECH; mcount++) {
966 			char		authtype[MECH_MAXATNAME];
967 
968 			if (!plist[mcount] && !clist[mcount])
969 				continue;
970 
971 			__nis_mechalias2authtype(CURMECH->alias, authtype,
972 							MECH_MAXATNAME);
973 			if (!authtype) {
974 				fprintf(stderr,
975 				"Could not generate auth_type for %s.\n",
976 					CURMECH->alias);
977 				continue;
978 			}
979 
980 			snprintf(pkent, MAXPKENTLEN, "%s:%s:%d",
981 					plist[mcount], clist[mcount],
982 					CURMECH->algtype);
983 
984 			switch (dest_service) {
985 			case PK_LDAP:
986 				if (ldap_update(CURMECH->alias, netname,
987 						plist[mcount], clist[mcount],
988 						login_pw))
989 					fprintf(stderr,
990 			"%s: unable to update %s key in LDAP database\n",
991 						program_name, authtype);
992 				else
993 					ucount++;
994 				break;
995 
996 			case PK_NISPLUS:
997 				if (nisplus_update(nis_princ,
998 							authtype,
999 							plist[mcount],
1000 							clist[mcount]))
1001 					fprintf(stderr,
1002 			"%s: unable to update %s key in nisplus database\n",
1003 						program_name, authtype);
1004 				else
1005 					ucount++;
1006 				break;
1007 
1008 			case PK_YP:
1009 				/* Should never get here. */
1010 				break;
1011 
1012 			case PK_FILES:
1013 				/* Should never get here. */
1014 				break;
1015 			}
1016 		}
1017 	} else {
1018 		int	status = 0;
1019 
1020 		assert(plist[0] && clist[0]);
1021 		snprintf(pkent, MAXPKENTLEN, "%s:%s", plist[0], clist[0]);
1022 
1023 		switch (dest_service) {
1024 		case PK_LDAP:
1025 			if (ldap_update("dh192-0", netname,
1026 					plist[0], clist[0],
1027 					login_pw)) {
1028 				fprintf(stderr,
1029 			"%s: unable to update %s key in LDAP database\n",
1030 					program_name);
1031 				exit(1);
1032 			}
1033 			break;
1034 
1035 		case PK_NISPLUS:
1036 			assert(plist[0] && clist[0]);
1037 			if (nisplus_update(nis_princ,
1038 						AUTH_DES_AUTH_TYPE,
1039 						plist[0],
1040 						clist[0])) {
1041 					fprintf(stderr,
1042 			"%s: unable to update nisplus database\n",
1043 						program_name);
1044 					exit(1);
1045 			}
1046 			break;
1047 
1048 		case PK_YP:
1049 			if (status = yp_update(ypdomain, PKMAP,
1050 						YPOP_STORE, netname,
1051 						strlen(netname), pkent,
1052 						strlen(pkent))) {
1053 				fprintf(stderr,
1054 				"%s: unable to update NIS database (%u): %s\n",
1055 					program_name, status,
1056 					yperr_string(status));
1057 				exit(1);
1058 			}
1059 			break;
1060 
1061 		case PK_FILES:
1062 			if (localupdate(netname, PKFILE, YPOP_STORE, pkent)) {
1063 				fprintf(stderr,
1064 			"%s: hence, unable to update publickey database\n",
1065 					program_name);
1066 				exit(1);
1067 			}
1068 			break;
1069 
1070 		default:
1071 			/* Should never get here */
1072 			assert(0);
1073 		}
1074 		return;
1075 	}
1076 	if (!ucount) {
1077 		fprintf(stderr, "%s: unable to update any key-pairs for %s.\n",
1078 			program_name, pw->pw_name);
1079 		exit(1);
1080 	}
1081 }
1082 
1083 /* Check that someone else don't have the same auth information already */
1084 static
1085 nis_error
1086 auth_exists(char *princname, char *auth_name, char *auth_type, char *domain)
1087 {
1088 	char sname[NIS_MAXNAMELEN+1];
1089 	nis_result	*res;
1090 	nis_error status;
1091 	char *foundprinc;
1092 
1093 	(void) sprintf(sname, "[auth_name=%s,auth_type=%s],%s.%s",
1094 		auth_name, auth_type, CRED_TABLE, domain);
1095 	if (sname[strlen(sname)-1] != '.')
1096 		strcat(sname, ".");
1097 	/* Don't want FOLLOW_PATH here */
1098 	res = nis_list(sname,
1099 		MASTER_ONLY+USE_DGRAM+NO_AUTHINFO+FOLLOW_LINKS,
1100 		NULL, NULL);
1101 
1102 	status = res->status;
1103 	switch (res->status) {
1104 	case NIS_NOTFOUND:
1105 		break;
1106 	case NIS_TRYAGAIN:
1107 		(void) fprintf(stderr,
1108 			"%s: NIS+ server busy, try again later.\n",
1109 			program_name);
1110 		exit(1);
1111 		break;
1112 	case NIS_PERMISSION:
1113 		(void) fprintf(stderr,
1114 		"%s: insufficient permission to look up old credentials.\n",
1115 			program_name);
1116 		exit(1);
1117 		break;
1118 	case NIS_SUCCESS:
1119 		foundprinc = ENTRY_VAL(res->objects.objects_val, 0);
1120 		if (nis_dir_cmp(foundprinc, princname) != SAME_NAME) {
1121 			(void) fprintf(stderr,
1122 	"%s: %s credentials with auth_name '%s' already belong to '%s'.\n",
1123 			program_name, auth_type, auth_name, foundprinc);
1124 			exit(1);
1125 		}
1126 		break;
1127 	default:
1128 		(void) fprintf(stderr,
1129 			"%s: error looking at cred table, NIS+ error: %s\n",
1130 			program_name, nis_sperrno(res->status));
1131 		exit(1);
1132 	}
1133 	nis_freeresult(res);
1134 	return (status);
1135 }
1136 
1137 
1138 /* Check whether this principal already has this type of credentials */
1139 static nis_error
1140 cred_exists(const char *nisprinc, const char *flavor, const char *domain)
1141 {
1142 	char sname[NIS_MAXNAMELEN+1];
1143 	nis_result	*res;
1144 	nis_error status;
1145 
1146 	snprintf(sname, NIS_MAXNAMELEN,
1147 			"[cname=\"%s\",auth_type=%s],%s.%s",
1148 			nisprinc, flavor, CRED_TABLE, domain);
1149 	if (sname[strlen(sname)-1] != '.')
1150 		strcat(sname, ".");
1151 
1152 	/* Don't want FOLLOW_PATH here */
1153 	res = nis_list(sname,
1154 				MASTER_ONLY+USE_DGRAM+NO_AUTHINFO+FOLLOW_LINKS,
1155 				NULL, NULL);
1156 
1157 	status = res->status;
1158 	switch (status) {
1159 	case NIS_NOTFOUND:
1160 		break;
1161 	case NIS_TRYAGAIN:
1162 		fprintf(stderr,
1163 			"%s: NIS+ server busy, try again later.\n",
1164 			program_name);
1165 		exit(1);
1166 		break;
1167 	case NIS_PERMISSION:
1168 		(void) fprintf(stderr,
1169 		"%s: insufficient permission to look at credentials table\n",
1170 			program_name);
1171 		exit(1);
1172 		break;
1173 	case NIS_SUCCESS:
1174 	case NIS_S_SUCCESS:
1175 		break;
1176 	default:
1177 		(void) fprintf(stderr,
1178 			"%s: error looking at cred table, NIS+ error: %s\n",
1179 			program_name, nis_sperrno(res->status));
1180 		exit(1);
1181 	}
1182 	nis_freeresult(res);
1183 	return (status);
1184 }
1185 
1186 
1187 static int
1188 modify_cred_obj(nis_object *obj, char *domain)
1189 {
1190 	int status = 0;
1191 	char sname[NIS_MAXNAMELEN+1];
1192 	nis_result	*res;
1193 
1194 	(void) sprintf(sname, "%s.%s", CRED_TABLE, domain);
1195 	res = nis_modify_entry(sname, obj, 0);
1196 	switch (res->status) {
1197 	case NIS_TRYAGAIN:
1198 		(void) fprintf(stderr,
1199 			"%s: NIS+ server busy, try again later.\n",
1200 			program_name);
1201 		exit(1);
1202 		break;
1203 	case NIS_PERMISSION:
1204 		(void) fprintf(stderr,
1205 			"%s: insufficient permission to update credentials.\n",
1206 			program_name);
1207 		exit(1);
1208 		break;
1209 	case NIS_SUCCESS:
1210 		status = 1;
1211 		break;
1212 	default:
1213 		(void) fprintf(stderr,
1214 			"%s: error modifying credential, NIS+ error: %s.\n",
1215 			program_name, nis_sperrno(res->status));
1216 		exit(1);
1217 	}
1218 	nis_freeresult(res);
1219 	return (status);
1220 }
1221 
1222 
1223 static int
1224 add_cred_obj(nis_object *obj, char *domain)
1225 {
1226 	int status = 0;
1227 	char sname[NIS_MAXNAMELEN+1];
1228 	nis_result	*res;
1229 
1230 	/* Assume check for cred_exists performed already */
1231 
1232 	(void) sprintf(sname, "%s.%s", CRED_TABLE, domain);
1233 	res = nis_add_entry(sname, obj, 0);
1234 	switch (res->status) {
1235 	case NIS_TRYAGAIN:
1236 		(void) fprintf(stderr,
1237 			"%s: NIS+ server busy, try again later.\n",
1238 			program_name);
1239 		exit(1);
1240 		break;
1241 	case NIS_PERMISSION:
1242 		(void) fprintf(stderr,
1243 			"%s: insufficient permission to update credentials.\n",
1244 			program_name);
1245 		exit(1);
1246 		break;
1247 	case NIS_SUCCESS:
1248 		status = 1;
1249 		break;
1250 	default:
1251 		(void) fprintf(stderr,
1252 			"%s: error creating credential, NIS+ error: %s.\n",
1253 			program_name, nis_sperrno(res->status));
1254 		exit(1);
1255 	}
1256 	nis_freeresult(res);
1257 	return (status);
1258 }
1259 
1260 
1261 /* Update NIS+ table with new key information */
1262 static int
1263 nisplus_update(nis_name nis_princ, char *authtype, char *public, char *crypt)
1264 {
1265 	nis_object	*obj = init_entry();
1266 	int		status;
1267 	bool_t		addition;
1268 	char		*userdomain, *cmpdomain, *domain;
1269 
1270 	if (!(userdomain = strchr(netname, '@'))) {
1271 		fprintf(stderr, "%s: invalid netname: '%s'.\n",
1272 			program_name, netname);
1273 		exit(1);
1274 	}
1275 	userdomain++;
1276 
1277 	cmpdomain = strdup(userdomain);
1278 	if (cmpdomain[strlen(cmpdomain) - 1] != '.')
1279 		strcat(cmpdomain, ".");
1280 
1281 	domain = nis_domain_of(nis_princ);
1282 	if (strcasecmp(domain, cmpdomain) != 0)
1283 		domain = nis_local_directory();
1284 
1285 	if (!sanity_checks(nis_princ, domain, authtype))
1286 		exit(1);
1287 
1288 	addition = (cred_exists(nis_princ, authtype, domain) == NIS_NOTFOUND);
1289 
1290 	ENTRY_VAL(obj, 0) = nis_princ;
1291 	ENTRY_LEN(obj, 0) = strlen(nis_princ) + 1;
1292 
1293 	ENTRY_VAL(obj, 1) = authtype;
1294 	ENTRY_LEN(obj, 1) = strlen(authtype) + 1;
1295 
1296 	ENTRY_VAL(obj, 2) = netname;
1297 	ENTRY_LEN(obj, 2) = strlen(netname) + 1;
1298 
1299 	ENTRY_VAL(obj, 3) = public;
1300 	ENTRY_LEN(obj, 3) = strlen(public) + 1;
1301 
1302 	ENTRY_VAL(obj, 4) = crypt;
1303 	ENTRY_LEN(obj, 4) = strlen(crypt) + 1;
1304 
1305 	if (addition) {
1306 		obj->zo_owner = nis_princ;
1307 		obj->zo_group = nis_local_group();
1308 		obj->zo_domain = domain;
1309 		/* owner: r, group: rmcd */
1310 		obj->zo_access = ((NIS_READ_ACC<<16)|
1311 				(NIS_READ_ACC|NIS_MODIFY_ACC|NIS_CREATE_ACC|
1312 				NIS_DESTROY_ACC)<<8);
1313 		status = add_cred_obj(obj, domain);
1314 	} else {
1315 		obj->EN_data.en_cols.en_cols_val[3].ec_flags |= EN_MODIFIED;
1316 		obj->EN_data.en_cols.en_cols_val[4].ec_flags |= EN_MODIFIED;
1317 		status = modify_cred_obj(obj, domain);
1318 	}
1319 	return (status == 1 ? 0 : 1);
1320 }
1321 
1322 
1323 void
1324 addmechtolist(char *mechtype)
1325 {
1326 	mechanism_t	**realmechlist;
1327 	int		i;
1328 
1329 	if (realmechlist = __nis_get_mechanisms(FALSE)) {
1330 		/* Match requested mech with list */
1331 		for (i = 0; realmechlist[i]; i++) {
1332 			if (realmechlist[i]->alias)
1333 				if (strcmp(realmechlist[i]->alias, mechtype)
1334 				    == 0) {
1335 					/*
1336 					 * Match, add it to the mechs.
1337 					 * Don't worry about qop or
1338 					 * secserv since they are not
1339 					 * used by chkey.
1340 					 */
1341 					numspecmech++;
1342 					if ((mechs =
1343 						(mechanism_t **)realloc(mechs,
1344 				sizeof (mechanism_t *) * (numspecmech + 1))) ==
1345 					    NULL) {
1346 						perror("Can not change keys");
1347 						exit(1);
1348 					}
1349 
1350 					if ((mechs[numspecmech - 1] =
1351 		(mechanism_t *)malloc(sizeof (mechanism_t))) == NULL) {
1352 						perror("Can not change keys");
1353 						exit(1);
1354 					}
1355 					if (realmechlist[i]->mechname)
1356 					mechs[numspecmech - 1]->mechname =
1357 					strdup(realmechlist[i]->mechname);
1358 					if (realmechlist[i]->alias)
1359 					mechs[numspecmech - 1]->alias =
1360 						strdup(realmechlist[i]->alias);
1361 					mechs[numspecmech - 1]->keylen =
1362 						realmechlist[i]->keylen;
1363 					mechs[numspecmech - 1]->algtype =
1364 						realmechlist[i]->algtype;
1365 					mechs[numspecmech] = NULL;
1366 					__nis_release_mechanisms(realmechlist);
1367 					return;
1368 				}
1369 		}
1370 
1371 		fprintf(stderr,
1372 		"WARNING: Mechanism '%s' not configured, skipping...\n",
1373 			mechtype);
1374 		__nis_release_mechanisms(realmechlist);
1375 		return;
1376 	}
1377 	fprintf(stderr,
1378 		"WARNING: Mechanism '%s' not configured, skipping...\n",
1379 		mechtype);
1380 }
1381 
1382 
1383 int
1384 main(int argc, char **argv)
1385 {
1386 	int		c, mcount;
1387 	uid_t		uid;
1388 	uid_t		orig_euid;
1389 	char		*service = NULL;
1390 	program_name = argv[0];
1391 
1392 	mechs = __nis_get_mechanisms(FALSE);
1393 
1394 	while ((c = getopt(argc, argv, "fps:m:")) != -1) {
1395 		switch (c) {
1396 		case 'f':
1397 			/*
1398 			 * Not documented as of on1093.
1399 			 * Temporarily supported
1400 			 */
1401 			force++;
1402 			break;
1403 		case 'p':
1404 			makenew = FALSE;
1405 			break;
1406 		case 's':
1407 			if (!service)
1408 				service = strdup(optarg);
1409 			else
1410 				usage();
1411 			break;
1412 		case 'm':
1413 			if (mechs && specmech == FALSE) {
1414 				__nis_release_mechanisms(mechs);
1415 				mechs = NULL;
1416 			}
1417 			specmech = TRUE;
1418 			addmechtolist(optarg);
1419 			break;
1420 		default:
1421 			usage();
1422 		}
1423 	}
1424 
1425 	if (optind < argc)
1426 		usage();
1427 
1428 	dest_service = get_pk_source(service);
1429 
1430 	if (!(netname = malloc(MAXNETNAMELEN + 1))) {
1431 		fprintf(stderr, "%s: Malloc failure.\n", program_name);
1432 		exit(1);
1433 	}
1434 	if (!__getnetnamebyuid(netname, uid = getuid())) {
1435 		fprintf(stderr, "%s: cannot generate netname for uid %d\n",
1436 			program_name, uid);
1437 		exit(1);
1438 	}
1439 	sec_domain = strdup(strchr(netname, '@') + 1);
1440 	getdomainname(local_domain, MAXNETNAMELEN);
1441 
1442 	if (makenew)
1443 		fprintf(stdout, "Generating new key for '%s'.\n", netname);
1444 	else
1445 		fprintf(stdout, "Reencrypting key for '%s'.\n", netname);
1446 
1447 	if (mechs) {
1448 		if (dest_service == PK_YP || dest_service == PK_FILES) {
1449 			fprintf(stderr,
1450 		"%s: can not add non-DES public keys to %s, skipping.\n",
1451 				program_name, service);
1452 			__nis_release_mechanisms(mechs);
1453 			mechs = NULL;
1454 			initkeylist(TRUE);
1455 		} else
1456 			initkeylist(FALSE);
1457 	} else
1458 		initkeylist(TRUE);
1459 
1460 	uid = getuid();
1461 	orig_euid = geteuid();
1462 
1463 	/* Get password information */
1464 	if ((pw = getpwuid(uid)) == NULL) {
1465 		fprintf(stderr,
1466 			"%s: Can not find passwd information for %d.\n",
1467 			program_name, uid);
1468 		exit(1);
1469 	}
1470 
1471 	/* Set eUID to user */
1472 	seteuid(uid);
1473 
1474 	/* Obtain a list of decrypted secret keys */
1475 	getsecrets();
1476 
1477 	/* Keylogin user if not already done */
1478 	if (mechs) {
1479 		int mcount;
1480 
1481 		for (mcount = 0; CURMECH; mcount++) {
1482 			keylen_t	keylen = CURMECH->keylen;
1483 			algtype_t	algtype = CURMECH->algtype;
1484 
1485 			if (!key_secretkey_is_set_g(keylen, algtype) &&
1486 			    slist[mcount]) {
1487 				keylogin(CURMECH->keylen, CURMECH->algtype);
1488 				if ((uid == 0) && (makenew == FALSE))
1489 					write_rootkey(slist[mcount],
1490 					VALID_ALIAS(CURMECH->alias) ?
1491 							CURMECH->alias :
1492 							"",
1493 							keylen, algtype);
1494 			}
1495 		}
1496 	} else {
1497 		assert(slist[0]);
1498 		if (!key_secretkey_is_set()) {
1499 			keylogin_des();
1500 			if ((uid == 0) && (makenew == FALSE))
1501 				write_rootkey(slist[0], "des", 192, 0);
1502 		}
1503 	}
1504 
1505 	/* Set eUID back to root */
1506 	(void) seteuid(orig_euid);
1507 
1508 	/*
1509 	 * Call getspnam() after the keylogin has been done so we have
1510 	 * the best chance of having read access to the encrypted pw.
1511 	 *
1512 	 * The eUID must be 0 for the getspnam() so the name service
1513 	 * switch can handle the following eUID sensitive cases:
1514 	 *
1515 	 *	files/compat:	read /etc/shadow
1516 	 *
1517 	 *	nisplus:	try to read the encrypted pw as the root
1518 	 *			principal and if that fails, and if the
1519 	 *			user's secret key is set, seteuid(user)
1520 	 *			and retry the read.
1521 	 */
1522 	if ((spw = getspnam(pw->pw_name)) == 0) {
1523 
1524 		/* Set eUID back to user */
1525 		(void) seteuid(uid);
1526 
1527 		(void) fprintf(stderr,
1528 			"%s: cannot find shadow entry for %s.\n",
1529 			program_name, pw->pw_name);
1530 		exit(1);
1531 	}
1532 
1533 	/* Set eUID back to user */
1534 	(void) seteuid(uid);
1535 
1536 	if (strcmp(spw->sp_pwdp, "*NP*") == 0) {
1537 		(void) fprintf(stderr,
1538 		"%s: do not have read access to the passwd field for %s\n",
1539 				program_name, pw->pw_name);
1540 		exit(1);
1541 	}
1542 
1543 	/*
1544 	 * force will be only supported for a while
1545 	 * 	-- it is NOT documented as of s1093
1546 	 */
1547 	if (force) {
1548 		char	*prompt = "Please enter New password:";
1549 
1550 		login_pw = getpass(prompt);
1551 		if (!login_pw || !(strlen(login_pw))) {
1552 			fprintf(stderr, "%s: key-pair(s) unchanged for %s.\n",
1553 				program_name, pw->pw_name);
1554 			exit(1);
1555 		}
1556 	} else {
1557 		/*
1558 		 * Reconsile rpc_pws and login_pw.
1559 		 *
1560 		 * This function will either return with login_pw == rpc_pw
1561 		 * (and thus, the new pw to encrypt keys) or it will exit.
1562 		 */
1563 		cmp_passwd();
1564 	}
1565 
1566 	if (makenew)
1567 		makenewkeys();
1568 	else
1569 		getpublics();
1570 
1571 	encryptkeys();
1572 
1573 	storekeys();
1574 
1575 	if (makenew) {
1576 		if (uid == 0) {
1577 			if (mechs) {
1578 				for (mcount = 0; CURMECH; mcount++) {
1579 					if (!slist[mcount])
1580 						continue;
1581 					write_rootkey(slist[mcount],
1582 							CURMECH->alias,
1583 							CURMECH->keylen,
1584 							CURMECH->algtype);
1585 				}
1586 			} else {
1587 				assert(slist[0]);
1588 				write_rootkey(slist[0], "des", 192, 0);
1589 			}
1590 		}
1591 		if (mechs) {
1592 			for (mcount = 0; CURMECH; mcount++)
1593 				keylogin(CURMECH->keylen,
1594 						CURMECH->algtype);
1595 		} else
1596 			keylogin_des();
1597 	}
1598 	return (0);
1599 }
1600