xref: /illumos-gate/usr/src/cmd/keyserv/keylogin.c (revision 4ab75253616c6d68e967c10221bb663c0bfa99df)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved  	*/
29 
30 /*
31  * University Copyright- Copyright (c) 1982, 1986, 1988
32  * The Regents of the University of California
33  * All Rights Reserved
34  *
35  * University Acknowledgment- Portions of this document are derived from
36  * software developed by the University of California, Berkeley, and its
37  * contributors.
38  */
39 
40 #pragma ident	"%Z%%M%	%I%	%E% SMI"
41 
42 /*
43  * Set secret key on local machine
44  */
45 #include <stdio.h>
46 #include <rpc/rpc.h>
47 #include <rpc/key_prot.h>
48 #include <nfs/nfs.h>				/* to revoke existing creds */
49 #include <nfs/nfssys.h>
50 #include <string.h>
51 #include <rpcsvc/nis_dhext.h>
52 
53 #define	ROOTKEY_FILE "/etc/.rootkey"
54 #define	ROOTKEY_FILE_BACKUP	"/etc/.rootkey.bak"
55 /* Should last until 16384-bit DH keys */
56 #define	MAXROOTKEY_LINE_LEN	4224
57 #define	MAXROOTKEY_LEN		4096
58 
59 extern int key_setnet_g();
60 
61 static void logout_curr_key();
62 static int mkrootkey;
63 
64 static char *sec_domain = NULL;
65 static char local_domain[MAXNETNAMELEN + 1];
66 
67 /*
68  * fgets is broken in that if it reads a NUL character it will always return
69  * EOF.  This replacement can deal with NULs
70  */
71 static char *
72 fgets_ignorenul(char *s, int n, FILE *stream)
73 {
74 	int fildes = fileno(stream);
75 	int i = 0;
76 	int rs = 0;
77 	char c;
78 
79 	if (fildes < 0)
80 		return (NULL);
81 
82 	while (i < n - 1) {
83 		rs = read(fildes, &c, 1);
84 		switch (rs) {
85 		case 1:
86 			break;
87 		case 0:
88 			/* EOF */
89 			if (i > 0)
90 				s[i] = '\0';
91 			return (NULL);
92 			break;
93 		default:
94 			return (NULL);
95 		}
96 		switch (c) {
97 		case '\0':
98 			break;
99 		case '\n':
100 			s[i] = c;
101 			s[++i] = '\0';
102 			return (s);
103 		default:
104 		if (c != '\0')
105 			s[i++] = c;
106 		}
107 	}
108 	s[i] = '\0';
109 	return (s);
110 }
111 
112 
113 /* write unencrypted secret key into root key file */
114 static void
115 write_rootkey(char *secret, char *flavor, keylen_t keylen, algtype_t algtype)
116 {
117 	char		line[MAXROOTKEY_LINE_LEN];
118 	char		keyent[MAXROOTKEY_LEN];
119 	algtype_t	atent;
120 	int		rootfd, bakfd, hexkeybytes;
121 	bool_t		lineone = TRUE;
122 	bool_t		gotit = FALSE;
123 	FILE		*rootfile, *bakfile;
124 
125 	unlink(ROOTKEY_FILE_BACKUP);
126 	if ((rename(ROOTKEY_FILE, ROOTKEY_FILE_BACKUP)) < 0) {
127 		if ((bakfd = creat(ROOTKEY_FILE_BACKUP, 0600)) < 0) {
128 			perror("Could not create /etc/.rootkey.bak");
129 			goto rootkey_err;
130 		}
131 		close(bakfd);
132 	}
133 
134 	if ((rootfd = open(ROOTKEY_FILE, O_WRONLY+O_CREAT, 0600)) < 0) {
135 		perror("Could not open /etc/.rootkey for writing");
136 		fprintf(stderr,
137 			"Attempting to restore original /etc/.rootkey\n");
138 		(void) rename(ROOTKEY_FILE_BACKUP, ROOTKEY_FILE);
139 		goto rootkey_err;
140 	}
141 	if (!(rootfile = fdopen(rootfd, "w"))) {
142 		perror("Could not open /etc/.rootkey for writing");
143 		fprintf(stderr,
144 			"Attempting to restore original /etc/.rootkey\n");
145 		close(rootfd);
146 		unlink(ROOTKEY_FILE);
147 		rename(ROOTKEY_FILE_BACKUP, ROOTKEY_FILE);
148 		goto rootkey_err;
149 	}
150 	if (!(bakfile = fopen(ROOTKEY_FILE_BACKUP, "r"))) {
151 		perror("Could not open /etc/.rootkey.bak for reading");
152 		fprintf(stderr,
153 			"Attempting to restore original /etc/.rootkey\n");
154 		(void) fclose(rootfile);
155 		unlink(ROOTKEY_FILE);
156 		rename(ROOTKEY_FILE_BACKUP, ROOTKEY_FILE);
157 		goto rootkey_err;
158 	}
159 
160 	hexkeybytes = ((keylen + 7) / 8) * 2;
161 
162 	while (fgets_ignorenul(line, MAXROOTKEY_LINE_LEN, bakfile)) {
163 		(void) sscanf(line, "%s %d", keyent, &atent);
164 		/*
165 		 * 192-bit keys always go on the first line
166 		 */
167 		if (lineone) {
168 			lineone = FALSE;
169 			if (keylen == 192) {
170 				gotit = TRUE;
171 				fprintf(rootfile, "%s\n", secret);
172 			} else
173 				fprintf(rootfile, "%s", line);
174 			(void) fflush(rootfile);
175 		} else {
176 			if ((strlen(keyent) == hexkeybytes) &&
177 			    (atent == algtype)) {
178 				/*
179 				 * Silently remove lines with the same
180 				 * keylen/algtype
181 				 */
182 				if (gotit)
183 					continue;
184 				else
185 					gotit = TRUE;
186 
187 				fprintf(rootfile, "%s %d\n", secret, algtype);
188 			} else
189 				fprintf(rootfile, "%s", line);
190 			(void) fflush(rootfile);
191 		}
192 	}
193 
194 	/* Append key to rootkey file */
195 	if (!gotit) {
196 		if (keylen == 192)
197 			fprintf(rootfile, "%s\n", secret);
198 		else {
199 			if (lineone)
200 				fprintf(rootfile, "\n");
201 			fprintf(rootfile, "%s %d\n", secret, algtype);
202 		}
203 	}
204 	(void) fflush(rootfile);
205 	fclose(rootfile);
206 	fclose(bakfile);
207 	unlink(ROOTKEY_FILE_BACKUP);
208 	if (keylen == 192)
209 		fprintf(stderr, "Wrote secret key into %s\n", ROOTKEY_FILE);
210 	else
211 		fprintf(stderr, "Wrote %s key into %s\n", flavor,
212 			ROOTKEY_FILE);
213 	return;
214 
215 rootkey_err:
216 	fprintf(stderr, "WARNING: Could not write %s key to /etc/.rootkey\n",
217 		flavor);
218 }
219 
220 /* Perform AUTH_DES keylogin */
221 static int
222 oldkeylogin(char *fullname, char *pass)
223 {
224 	char			secret[HEXKEYBYTES+1];
225 	struct key_netstarg	netst;
226 
227 		if (getsecretkey(fullname, secret, pass) == 0) {
228 			fprintf(stderr, "Could not find %s's secret key\n",
229 				fullname);
230 			if (sec_domain && *sec_domain &&
231 				strcasecmp(sec_domain, local_domain)) {
232 				fprintf(stderr,
233 "The system default domain '%s' is different from the Secure RPC\n\
234 domain %s where the key is stored.  The Secure RPC domainname is\n\
235 defined by the directory object stored in the /var/nis/NIS_COLD_START file.\n\
236 If you need to change this Secure RPC domainname, please use the nisinit(1M)\n\
237 command with the `-k` option.\n", local_domain, sec_domain);
238 			} else {
239 				fprintf(stderr,
240 		"Make sure the secret key is stored in domain %s\n",
241 				local_domain);
242 			}
243 			return (1);
244 		}
245 
246 		if (secret[0] == 0) {
247 			fprintf(stderr, "Password incorrect for %s\n",
248 				fullname);
249 			return (1);
250 		}
251 		/* revoke any existing (lingering) credentials... */
252 		logout_curr_key();
253 
254 		memcpy(netst.st_priv_key, secret, HEXKEYBYTES);
255 		memset(secret, 0, HEXKEYBYTES);
256 
257 		netst.st_pub_key[0] = 0;
258 		netst.st_netname = strdup(fullname);
259 
260 		/* do actual key login */
261 		if (key_setnet(&netst) < 0) {
262 			fprintf(stderr, "Could not set %s's secret key\n",
263 				fullname);
264 			fprintf(stderr, "May be the keyserv is down?\n");
265 			if (mkrootkey == 0)   /* nothing else to do */
266 				return (1);
267 		}
268 
269 		/* write unencrypted secret key into root key file */
270 		if (mkrootkey)
271 			write_rootkey(netst.st_priv_key, "des", 192, 0);
272 
273 		return (0);
274 }
275 
276 /*
277  * Revokes the existing credentials for Secure-RPC and Secure-NFS.
278  * This should only be called if the user entered the correct password;
279  * sorta like the way "su" doesn't force a login if you enter the wrong
280  * password.
281  */
282 
283 static void
284 logout_curr_key()
285 {
286 	static char		secret[HEXKEYBYTES + 1];
287 	struct nfs_revauth_args	nra;
288 
289 	/*
290 	 * try to revoke the existing key/credentials, assuming
291 	 * one exists.  this will effectively mark "stale" any
292 	 * cached credientials...
293 	 */
294 	if (key_setsecret(secret) < 0) {
295 		return;
296 	}
297 
298 	/*
299 	 * it looks like a credential already existed, so try and
300 	 * revoke any lingering Secure-NFS privledges.
301 	 */
302 
303 	nra.authtype = AUTH_DES;
304 	nra.uid = getuid();
305 
306 	(void) _nfssys(NFS_REVAUTH, &nra);
307 }
308 
309 void
310 usage(cmd)
311 	char *cmd;
312 {
313 	fprintf(stderr, "usage: %s [-r]\n", cmd);
314 	exit(1);
315 }
316 
317 
318 int
319 main(int argc, char *argv[])
320 {
321 	char		secret[4096];
322 	char		fullname[MAXNETNAMELEN + 1];
323 	char		*getpass();
324 	char		*pass;
325 	int		i = 0;
326 	mechanism_t	**mechlist;
327 
328 	if (argc == 1)
329 		mkrootkey = 0;
330 	else if (argc == 2 && (strcmp(argv[1], "-r") == 0)) {
331 		if (geteuid() != 0) {
332 			fprintf(stderr, "Must be root to use -r option.\n");
333 			exit(1);
334 		}
335 		mkrootkey = 1;
336 	} else
337 		usage(argv[0]);
338 
339 	if (getnetname(fullname) == 0) {
340 		fprintf(stderr, "Could not generate netname\n");
341 		exit(1);
342 	}
343 	sec_domain = strdup(strchr(fullname, '@') + 1);
344 	getdomainname(local_domain, MAXNETNAMELEN);
345 
346 	if (!(pass = getpass("Password:")))
347 		exit(1);
348 
349 	if (mechlist = __nis_get_mechanisms(FALSE)) {
350 		while (mechlist[i]) {
351 			char		*alias;
352 
353 			if (AUTH_DES_COMPAT_CHK(mechlist[i])) {
354 				(void) oldkeylogin(fullname, pass);
355 				i++;
356 				continue;
357 			}
358 
359 			if (VALID_ALIAS(mechlist[i]->alias))
360 				alias = mechlist[i]->alias;
361 			else
362 				alias = "";
363 
364 			if (getsecretkey_g(fullname, mechlist[i]->keylen,
365 						mechlist[i]->algtype, secret,
366 						(((mechlist[i]->keylen / 7) +
367 						8) * 2) + 1, pass) == 0) {
368 				fprintf(stderr,
369 				"WARNING: Could not find %s's %s secret key\n",
370 					fullname, alias);
371 				i++;
372 				continue;
373 			}
374 
375 			if (secret[0] == 0) {
376 				fprintf(stderr,
377 				    "Password incorrect for %s's %s key.\n",
378 					fullname, alias);
379 				i++;
380 				continue;
381 			}
382 
383 			if (key_setnet_g(fullname, secret,
384 						mechlist[i]->keylen, NULL, 0,
385 						mechlist[i]->algtype) < 0) {
386 				fprintf(stderr,
387 				"Could not set %s's %s secret key\n",
388 					fullname, alias);
389 				fprintf(stderr,
390 					"May be the keyserv is down?\n");
391 				exit(1);
392 			}
393 
394 			if (mkrootkey)
395 				write_rootkey(secret, mechlist[i]->alias,
396 						mechlist[i]->keylen,
397 						mechlist[i]->algtype);
398 			i++;
399 		}
400 	} else
401 		exit(oldkeylogin(fullname, pass));
402 
403 	return (0);
404 }
405