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
63static char	CRED_TABLE[] = "cred.org_dir";
64static char	PKMAP[] = "publickey.byname";
65static 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
76bool_t		makenew = TRUE;   /* Make new keys or reencrypt existing */
77bool_t		specmech = FALSE; /* Specific mechs requested */
78bool_t		force = FALSE;
79int		dest_service = 0; /* To which nameservice do we store key(s) */
80
81char		*program_name;
82
83mechanism_t	**mechs = NULL;   /* List of DH mechanisms */
84char		**plist = NULL;	  /* List of public key(s) */
85char		**slist = NULL;	  /* List of secret key(s) */
86char		**clist = NULL;   /* List of encrypted secret key(s) */
87int		numspecmech = 0;  /* Number of mechanisms specified */
88
89struct passwd	*pw = NULL;	  /* passwd entry of user */
90struct spwd	*spw = NULL;	  /* shadow entry of user */
91
92char		*netname = NULL;  /* RPC netname of user */
93char		local_domain[MAXNETNAMELEN + 1];
94char		*sec_domain = NULL;
95
96char		**rpc_pws = NULL; /* List of S-RPC passwords */
97int		rpc_pw_count = 0; /* Number of passwords entered by user */
98char		*login_pw = NULL; /* Unencrypted login password */
99char		short_login_pw[DESCREDPASSLEN + 1];
100/* Short S-RPC password, which has first 8 chars of login_pw */
101
102static int add_cred_obj(nis_object *, char *);
103static void cmp_passwd();
104static void encryptkeys();
105static void error_msg();
106static char *fgets_ignorenul();
107static void getpublics();
108static void getrpcpws();
109static void getsecrets();
110static void initkeylist(bool_t);
111static void keylogin(keylen_t, algtype_t);
112static void keylogin_des();
113static void makenewkeys();
114static int modify_cred_obj(nis_object *, char *);
115static void storekeys();
116static void usage();
117static void write_rootkey();
118
119extern nis_object *init_entry();
120extern int get_pk_source(char *);
121extern int localupdate(char *, char *, uint_t, char *);
122extern int xencrypt();
123extern int xencrypt_g();
124extern int __gen_dhkeys();
125extern int key_setnet();
126extern int key_setnet_g();
127extern int key_secretkey_is_set_g();
128extern int __getnetnamebyuid();
129extern int getdomainname();
130extern int ldap_update(char *, char *, char *, char *, char *);
131
132
133static void
134error_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\
140domain %s where the key is stored. \n", local_domain, sec_domain);
141		exit(1);
142	}
143}
144
145
146static void
147usage()
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 */
156static void
157encryptkeys()
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 */
201static void
202initkeylist(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) */
229static void
230getpublics()
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) */
290static void
291makenewkeys()
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 */
358static void
359cmp_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. */
451static void
452getrpcpws(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	}
480rpc_pws[rpc_pw_count - 1] = cur_pw;
481}
482
483
484/* Retrieve the secret key(s) for the user and attempt to decrypt them */
485static void
486getsecrets()
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 */
613static void
614keylogin_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 */
655static void
656keylogin(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 */
689static char *
690fgets_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 */
732static void
733write_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
834rootkey_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 */
840static void
841storekeys()
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
972void
973addmechtolist(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
1033int
1034main(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