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