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