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