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