xref: /titanic_44/usr/src/lib/libnsl/rpc/auth_des.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
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  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
26 /* All Rights Reserved */
27 /*
28  * Portions of this source code were derived from Berkeley
29  * 4.3 BSD under license from the Regents of the University of
30  * California.
31  */
32 
33 #pragma ident	"%Z%%M%	%I%	%E% SMI"
34 
35 /*
36  * auth_des.c, client-side implementation of DES authentication
37  *
38  */
39 
40 #include "mt.h"
41 #include "rpc_mt.h"
42 #include <rpc/rpc.h>
43 #include <rpc/trace.h>
44 #include <rpc/des_crypt.h>
45 #include <syslog.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <synch.h>
49 #undef NIS
50 #include <rpcsvc/nis.h>
51 
52 #define	USEC_PER_SEC		1000000
53 #define	RTIME_TIMEOUT		5	/* seconds to wait for sync */
54 
55 #define	AUTH_PRIVATE(auth)	(struct ad_private *)auth->ah_private
56 #define	ALLOC(object_type)	(object_type *)mem_alloc(sizeof (object_type))
57 #define	FREE(ptr, size)		mem_free((char *)(ptr), (int)size)
58 #define	ATTEMPT(xdr_op)		if (!(xdr_op)) return (FALSE)
59 
60 #ifdef _KERNEL
61 #define	gettimeofday(tvp, tzp)	uniqtime(tvp)	/* fake system call */
62 #endif
63 
64 extern bool_t xdr_authdes_cred(XDR *, struct authdes_cred *);
65 extern bool_t xdr_authdes_verf(XDR *, struct authdes_verf *);
66 extern int key_encryptsession_pk();
67 
68 extern bool_t __rpc_get_time_offset(struct timeval *, nis_server *, char *,
69 						char **, char **);
70 static struct auth_ops *authdes_ops();
71 static bool_t authdes_refresh();
72 
73 /*
74  * This struct is pointed to by the ah_private field of an "AUTH *"
75  */
76 struct ad_private {
77 	char *ad_fullname;		/* client's full name */
78 	uint_t ad_fullnamelen;		/* length of name, rounded up */
79 	char *ad_servername;		/* server's full name */
80 	size_t ad_servernamelen;	/* length of name, rounded up */
81 	uint_t ad_window;		/* client specified window */
82 	bool_t ad_dosync;		/* synchronize? */
83 	char *ad_timehost;		/* remote host to sync with */
84 	struct timeval ad_timediff;	/* server's time - client's time */
85 	uint_t ad_nickname;		/* server's nickname for client */
86 	struct authdes_cred ad_cred;	/* storage for credential */
87 	struct authdes_verf ad_verf;	/* storage for verifier */
88 	struct timeval ad_timestamp;	/* timestamp sent */
89 	des_block ad_xkey;		/* encrypted conversation key */
90 	uchar_t ad_pkey[1024];		/* Servers actual public key */
91 	char *ad_netid;			/* Timehost netid */
92 	char *ad_uaddr;			/* Timehost uaddr */
93 	nis_server *ad_nis_srvr;	/* NIS+ server struct */
94 };
95 
96 AUTH *authdes_pk_seccreate(const char *, netobj *, uint_t, const char *,
97 				const des_block *, nis_server *);
98 
99 /*
100  * documented version of authdes_seccreate
101  */
102 /*
103  *	servername: 	network name of server
104  *	win:		time to live
105  *	timehost:	optional hostname to sync with
106  *	ckey:		optional conversation key to use
107  */
108 
109 AUTH *
110 authdes_seccreate(const char *servername, const uint_t win,
111 	const char *timehost, const des_block *ckey)
112 {
113 	uchar_t	pkey_data[1024];
114 	netobj	pkey;
115 	AUTH	*dummy;
116 
117 	trace1(TR_authdes_seccreate, 0);
118 	if (! getpublickey(servername, (char *)pkey_data)) {
119 		syslog(LOG_ERR,
120 			"authdes_seccreate: no public key found for %s",
121 			servername);
122 
123 		trace1(TR_authdes_seccreate, 1);
124 		return (NULL);
125 	}
126 
127 	pkey.n_bytes = (char *)pkey_data;
128 	pkey.n_len = (uint_t)strlen((char *)pkey_data) + 1;
129 	dummy = authdes_pk_seccreate(servername, &pkey, win, timehost,
130 					ckey, NULL);
131 	return (dummy);
132 }
133 
134 /*
135  * Slightly modified version of authdes_seccreate which takes the public key
136  * of the server principal as an argument. This spares us a call to
137  * getpublickey() which in the nameserver context can cause a deadlock.
138  */
139 
140 AUTH *
141 authdes_pk_seccreate(const char *servername, netobj *pkey, uint_t window,
142 	const char *timehost, const des_block *ckey, nis_server *srvr)
143 {
144 	AUTH *auth;
145 	struct ad_private *ad;
146 	char namebuf[MAXNETNAMELEN+1];
147 
148 	/*
149 	 * Allocate everything now
150 	 */
151 	trace2(TR_authdes_pk_seccreate, 0, window);
152 	auth = ALLOC(AUTH);
153 	if (auth == NULL) {
154 		syslog(LOG_ERR, "authdes_pk_seccreate: out of memory");
155 		trace1(TR_authdes_pk_seccreate, 1);
156 		return (NULL);
157 	}
158 	ad = ALLOC(struct ad_private);
159 	if (ad == NULL) {
160 		syslog(LOG_ERR, "authdes_pk_seccreate: out of memory");
161 		goto failed;
162 	}
163 	ad->ad_fullname = ad->ad_servername = NULL; /* Sanity reasons */
164 	ad->ad_timehost = NULL;
165 	ad->ad_netid = NULL;
166 	ad->ad_uaddr = NULL;
167 	ad->ad_nis_srvr = NULL;
168 	ad->ad_timediff.tv_sec = 0;
169 	ad->ad_timediff.tv_usec = 0;
170 	memcpy(ad->ad_pkey, pkey->n_bytes, pkey->n_len);
171 	if (!getnetname(namebuf))
172 		goto failed;
173 	ad->ad_fullnamelen = RNDUP((uint_t)strlen(namebuf));
174 	ad->ad_fullname = (char *)mem_alloc(ad->ad_fullnamelen + 1);
175 	ad->ad_servernamelen = strlen(servername);
176 	ad->ad_servername = (char *)mem_alloc(ad->ad_servernamelen + 1);
177 
178 	if (ad->ad_fullname == NULL || ad->ad_servername == NULL) {
179 		syslog(LOG_ERR, "authdes_seccreate: out of memory");
180 		goto failed;
181 	}
182 	if (timehost != NULL) {
183 		ad->ad_timehost = (char *)mem_alloc(strlen(timehost) + 1);
184 		if (ad->ad_timehost == NULL) {
185 			syslog(LOG_ERR, "authdes_seccreate: out of memory");
186 			goto failed;
187 		}
188 		memcpy(ad->ad_timehost, timehost, strlen(timehost) + 1);
189 		ad->ad_dosync = TRUE;
190 	} else if (srvr != NULL) {
191 		ad->ad_nis_srvr = srvr;	/* transient */
192 		ad->ad_dosync = TRUE;
193 	} else {
194 		ad->ad_dosync = FALSE;
195 	}
196 	memcpy(ad->ad_fullname, namebuf, ad->ad_fullnamelen + 1);
197 	memcpy(ad->ad_servername, servername, ad->ad_servernamelen + 1);
198 	ad->ad_window = window;
199 	if (ckey == NULL) {
200 		if (key_gendes(&auth->ah_key) < 0) {
201 			syslog(LOG_ERR,
202 	"authdes_seccreate: keyserv(1m) is unable to generate session key");
203 			goto failed;
204 		}
205 	} else
206 		auth->ah_key = *ckey;
207 
208 	/*
209 	 * Set up auth handle
210 	 */
211 	auth->ah_cred.oa_flavor = AUTH_DES;
212 	auth->ah_verf.oa_flavor = AUTH_DES;
213 	auth->ah_ops = authdes_ops();
214 	auth->ah_private = (caddr_t)ad;
215 
216 	if (!authdes_refresh(auth, NULL)) {
217 		goto failed;
218 	}
219 	ad->ad_nis_srvr = NULL; /* not needed any longer */
220 	trace1(TR_authdes_pk_seccreate, 1);
221 	return (auth);
222 
223 failed:
224 	if (auth)
225 		FREE(auth, sizeof (AUTH));
226 	if (ad) {
227 		if (ad->ad_fullname)
228 			FREE(ad->ad_fullname, ad->ad_fullnamelen + 1);
229 		if (ad->ad_servername)
230 			FREE(ad->ad_servername, ad->ad_servernamelen + 1);
231 		if (ad->ad_timehost)
232 			FREE(ad->ad_timehost, strlen(ad->ad_timehost) + 1);
233 		if (ad->ad_netid)
234 			FREE(ad->ad_netid, strlen(ad->ad_netid) + 1);
235 		if (ad->ad_uaddr)
236 			FREE(ad->ad_uaddr, strlen(ad->ad_uaddr) + 1);
237 		FREE(ad, sizeof (struct ad_private));
238 	}
239 	trace1(TR_authdes_pk_seccreate, 1);
240 	return (NULL);
241 }
242 
243 /*
244  * Implement the five authentication operations
245  */
246 
247 /*
248  * 1. Next Verifier
249  */
250 /*ARGSUSED*/
251 static void
252 authdes_nextverf(AUTH *auth)
253 {
254 	trace1(TR_authdes_nextverf, 0);
255 	trace1(TR_authdes_nextverf, 1);
256 	/* what the heck am I supposed to do??? */
257 }
258 
259 
260 /*
261  * 2. Marshal
262  */
263 static bool_t
264 authdes_marshal(AUTH *auth, XDR *xdrs)
265 {
266 /* LINTED pointer alignment */
267 	struct ad_private *ad = AUTH_PRIVATE(auth);
268 	struct authdes_cred *cred = &ad->ad_cred;
269 	struct authdes_verf *verf = &ad->ad_verf;
270 	des_block cryptbuf[2];
271 	des_block ivec;
272 	int status;
273 	int len;
274 	rpc_inline_t *ixdr;
275 
276 	/*
277 	 * Figure out the "time", accounting for any time difference
278 	 * with the server if necessary.
279 	 */
280 	trace1(TR_authdes_marshal, 0);
281 	(void) gettimeofday(&ad->ad_timestamp, (struct timezone *)NULL);
282 	ad->ad_timestamp.tv_sec += ad->ad_timediff.tv_sec;
283 	ad->ad_timestamp.tv_usec += ad->ad_timediff.tv_usec;
284 	while (ad->ad_timestamp.tv_usec >= USEC_PER_SEC) {
285 		ad->ad_timestamp.tv_usec -= USEC_PER_SEC;
286 		ad->ad_timestamp.tv_sec++;
287 	}
288 
289 	/*
290 	 * XDR the timestamp and possibly some other things, then
291 	 * encrypt them.
292 	 */
293 	ixdr = (rpc_inline_t *)cryptbuf;
294 	IXDR_PUT_INT32(ixdr, ad->ad_timestamp.tv_sec);
295 	IXDR_PUT_INT32(ixdr, ad->ad_timestamp.tv_usec);
296 	if (ad->ad_cred.adc_namekind == ADN_FULLNAME) {
297 		IXDR_PUT_U_INT32(ixdr, ad->ad_window);
298 		IXDR_PUT_U_INT32(ixdr, ad->ad_window - 1);
299 		ivec.key.high = ivec.key.low = 0;
300 		status = cbc_crypt((char *)&auth->ah_key, (char *)cryptbuf,
301 				2 * sizeof (des_block),
302 				DES_ENCRYPT | DES_HW, (char *)&ivec);
303 	} else {
304 		status = ecb_crypt((char *)&auth->ah_key, (char *)cryptbuf,
305 				sizeof (des_block),
306 				DES_ENCRYPT | DES_HW);
307 	}
308 	if (DES_FAILED(status)) {
309 		syslog(LOG_ERR, "authdes_marshal: DES encryption failure");
310 		trace1(TR_authdes_marshal, 1);
311 		return (FALSE);
312 	}
313 	ad->ad_verf.adv_xtimestamp = cryptbuf[0];
314 	if (ad->ad_cred.adc_namekind == ADN_FULLNAME) {
315 		ad->ad_cred.adc_fullname.window = cryptbuf[1].key.high;
316 		ad->ad_verf.adv_winverf = cryptbuf[1].key.low;
317 	} else {
318 		ad->ad_cred.adc_nickname = ad->ad_nickname;
319 		ad->ad_verf.adv_winverf = 0;
320 	}
321 
322 	/*
323 	 * Serialize the credential and verifier into opaque
324 	 * authentication data.
325 	 */
326 	if (ad->ad_cred.adc_namekind == ADN_FULLNAME) {
327 		len = ((1 + 1 + 2 + 1)*BYTES_PER_XDR_UNIT + ad->ad_fullnamelen);
328 	} else {
329 		len = (1 + 1)*BYTES_PER_XDR_UNIT;
330 	}
331 
332 	if (ixdr = xdr_inline(xdrs, 2*BYTES_PER_XDR_UNIT)) {
333 		IXDR_PUT_INT32(ixdr, AUTH_DES);
334 		IXDR_PUT_INT32(ixdr, len);
335 	} else {
336 		ATTEMPT(xdr_putint32(xdrs, (int *)&auth->ah_cred.oa_flavor));
337 		ATTEMPT(xdr_putint32(xdrs, &len));
338 	}
339 	ATTEMPT(xdr_authdes_cred(xdrs, cred));
340 
341 	len = (2 + 1)*BYTES_PER_XDR_UNIT;
342 	if (ixdr = xdr_inline(xdrs, 2*BYTES_PER_XDR_UNIT)) {
343 		IXDR_PUT_INT32(ixdr, AUTH_DES);
344 		IXDR_PUT_INT32(ixdr, len);
345 	} else {
346 		ATTEMPT(xdr_putint32(xdrs, (int *)&auth->ah_verf.oa_flavor));
347 		ATTEMPT(xdr_putint32(xdrs, &len));
348 	}
349 	ATTEMPT(xdr_authdes_verf(xdrs, verf));
350 	trace1(TR_authdes_marshal, 1);
351 	return (TRUE);
352 }
353 
354 
355 /*
356  * 3. Validate
357  */
358 static bool_t
359 authdes_validate(AUTH *auth, struct opaque_auth *rverf)
360 {
361 /* LINTED pointer alignment */
362 	struct ad_private *ad = AUTH_PRIVATE(auth);
363 	struct authdes_verf verf;
364 	int status;
365 	uint32_t *ixdr;
366 	des_block buf;
367 
368 	trace1(TR_authdes_validate, 0);
369 	if (rverf->oa_length != (2 + 1) * BYTES_PER_XDR_UNIT) {
370 		trace1(TR_authdes_validate, 1);
371 		return (FALSE);
372 	}
373 /* LINTED pointer alignment */
374 	ixdr = (uint32_t *)rverf->oa_base;
375 	buf.key.high = (uint32_t)*ixdr++;
376 	buf.key.low = (uint32_t)*ixdr++;
377 	verf.adv_int_u = (uint32_t)*ixdr++;
378 
379 	/*
380 	 * Decrypt the timestamp
381 	 */
382 	status = ecb_crypt((char *)&auth->ah_key, (char *)&buf,
383 		sizeof (des_block), DES_DECRYPT | DES_HW);
384 
385 	if (DES_FAILED(status)) {
386 		syslog(LOG_ERR, "authdes_validate: DES decryption failure");
387 		trace1(TR_authdes_validate, 1);
388 		return (FALSE);
389 	}
390 
391 	/*
392 	 * xdr the decrypted timestamp
393 	 */
394 /* LINTED pointer alignment */
395 	ixdr = (uint32_t *)buf.c;
396 	verf.adv_timestamp.tv_sec = IXDR_GET_INT32(ixdr) + 1;
397 	verf.adv_timestamp.tv_usec = IXDR_GET_INT32(ixdr);
398 
399 	/*
400 	 * validate
401 	 */
402 	if (memcmp((char *)&ad->ad_timestamp, (char *)&verf.adv_timestamp,
403 		sizeof (struct timeval)) != 0) {
404 		syslog(LOG_DEBUG, "authdes_validate: verifier mismatch");
405 		trace1(TR_authdes_validate, 1);
406 		return (FALSE);
407 	}
408 
409 	/*
410 	 * We have a nickname now, let's use it
411 	 */
412 	ad->ad_nickname = verf.adv_nickname;
413 	ad->ad_cred.adc_namekind = ADN_NICKNAME;
414 	trace1(TR_authdes_validate, 1);
415 	return (TRUE);
416 }
417 
418 /*
419  * 4. Refresh
420  */
421 /*ARGSUSED*/
422 static bool_t
423 authdes_refresh(AUTH *auth, void *dummy)
424 {
425 /* LINTED pointer alignment */
426 	struct ad_private *ad = AUTH_PRIVATE(auth);
427 	struct authdes_cred *cred = &ad->ad_cred;
428 	int		ok;
429 	netobj		pkey;
430 
431 	trace1(TR_authdes_refresh, 0);
432 
433 	if (ad->ad_dosync) {
434 		ok = __rpc_get_time_offset(&ad->ad_timediff, ad->ad_nis_srvr,
435 					    ad->ad_timehost, &(ad->ad_uaddr),
436 					    &(ad->ad_netid));
437 		if (! ok) {
438 			/*
439 			 * Hope the clocks are synced!
440 			 */
441 			ad->ad_dosync = 0;
442 			syslog(LOG_DEBUG,
443 		    "authdes_refresh: unable to synchronize clock");
444 		}
445 	}
446 	ad->ad_xkey = auth->ah_key;
447 	pkey.n_bytes = (char *)(ad->ad_pkey);
448 	pkey.n_len = (uint_t)strlen((char *)ad->ad_pkey) + 1;
449 	if (key_encryptsession_pk(ad->ad_servername, &pkey, &ad->ad_xkey) < 0) {
450 		syslog(LOG_INFO,
451 	"authdes_refresh: keyserv(1m) is unable to encrypt session key");
452 		trace1(TR_authdes_refresh, 1);
453 		return (FALSE);
454 	}
455 	cred->adc_fullname.key = ad->ad_xkey;
456 	cred->adc_namekind = ADN_FULLNAME;
457 	cred->adc_fullname.name = ad->ad_fullname;
458 	trace1(TR_authdes_refresh, 1);
459 	return (TRUE);
460 }
461 
462 
463 /*
464  * 5. Destroy
465  */
466 static void
467 authdes_destroy(AUTH *auth)
468 {
469 /* LINTED pointer alignment */
470 	struct ad_private *ad = AUTH_PRIVATE(auth);
471 
472 	trace1(TR_authdes_destroy, 0);
473 	FREE(ad->ad_fullname, ad->ad_fullnamelen + 1);
474 	FREE(ad->ad_servername, ad->ad_servernamelen + 1);
475 	if (ad->ad_timehost)
476 		FREE(ad->ad_timehost, strlen(ad->ad_timehost) + 1);
477 	if (ad->ad_netid)
478 		FREE(ad->ad_netid, strlen(ad->ad_netid) + 1);
479 	if (ad->ad_uaddr)
480 		FREE(ad->ad_uaddr, strlen(ad->ad_uaddr) + 1);
481 	FREE(ad, sizeof (struct ad_private));
482 	FREE(auth, sizeof (AUTH));
483 	trace1(TR_authdes_destroy, 1);
484 }
485 
486 static struct auth_ops *
487 authdes_ops(void)
488 {
489 	static struct auth_ops ops;
490 	extern mutex_t ops_lock;
491 
492 	/* VARIABLES PROTECTED BY ops_lock: ops */
493 
494 	trace1(TR_authdes_ops, 0);
495 	mutex_lock(&ops_lock);
496 	if (ops.ah_nextverf == NULL) {
497 		ops.ah_nextverf = authdes_nextverf;
498 		ops.ah_marshal = authdes_marshal;
499 		ops.ah_validate = authdes_validate;
500 		ops.ah_refresh = authdes_refresh;
501 		ops.ah_destroy = authdes_destroy;
502 	}
503 	mutex_unlock(&ops_lock);
504 	trace1(TR_authdes_ops, 1);
505 	return (&ops);
506 }
507