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